rfacter 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rfacter.rb +1 -0
- data/lib/rfacter/cli.rb +13 -22
- data/lib/rfacter/config.rb +5 -0
- data/lib/rfacter/config/settings.rb +7 -0
- data/lib/rfacter/core/aggregate.rb +8 -14
- data/lib/rfacter/core/directed_graph.rb +6 -0
- data/lib/rfacter/core/resolvable.rb +19 -15
- data/lib/rfacter/core/suitable.rb +6 -11
- data/lib/rfacter/dsl.rb +11 -10
- data/lib/rfacter/facts/os.rb +48 -13
- data/lib/rfacter/factset.rb +116 -0
- data/lib/rfacter/node.rb +59 -1
- data/lib/rfacter/util/collection.rb +33 -25
- data/lib/rfacter/util/confine.rb +3 -0
- data/lib/rfacter/util/fact.rb +10 -37
- data/lib/rfacter/util/loader.rb +1 -4
- data/lib/rfacter/util/logger.rb +1 -0
- data/lib/rfacter/util/non_nullable.rb +1 -0
- data/lib/rfacter/util/normalization.rb +14 -31
- data/lib/rfacter/util/resolution.rb +9 -22
- data/lib/rfacter/util/values.rb +4 -0
- data/lib/rfacter/version.rb +1 -1
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7bc8b832480542b3e326f4f99389ea4d2b9968d
|
4
|
+
data.tar.gz: 37bad144b7ffbef453ad397ea39ed7ca89c1e43a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afef987a44e33cc8c83134de30694a0a3591f64984aac19906cbe35e27f090635c67af8202271af9b1f3ccfeafce07630690de3678eeafda008ec20e5336347e
|
7
|
+
data.tar.gz: fea2ba928eb78d00e736e35b53bd2f1106223aa612a4371bfe36bcb9e9d1b5731bfe05c5bc27aafc3d38769e9a2da79c27c01cb59d38091edb850219630f3127
|
data/lib/rfacter.rb
CHANGED
data/lib/rfacter/cli.rb
CHANGED
@@ -5,8 +5,13 @@ require 'rfacter'
|
|
5
5
|
|
6
6
|
require_relative 'config'
|
7
7
|
require_relative 'node'
|
8
|
+
require_relative 'factset'
|
8
9
|
require_relative 'util/collection'
|
9
10
|
|
11
|
+
# RFacter Command Line Interface module
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @since 0.1.0
|
10
15
|
module RFacter::CLI
|
11
16
|
extend SingleForwardable
|
12
17
|
|
@@ -20,31 +25,17 @@ module RFacter::CLI
|
|
20
25
|
@config.nodes['localhost'] = RFacter::Node.new('localhost')
|
21
26
|
end
|
22
27
|
|
23
|
-
logger.info('cli::run') { "Configured nodes: #{@config.nodes.values.map(&:
|
28
|
+
logger.info('cli::run') { "Configured nodes: #{@config.nodes.values.map(&:id)}" }
|
24
29
|
|
25
|
-
|
26
|
-
collection.load_all
|
30
|
+
factset = RFacter::Factset.new(@config.nodes.values)
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
n[name] = collection.value(name, node)
|
34
|
-
n
|
35
|
-
end
|
36
|
-
end
|
32
|
+
node_facts = if names.empty?
|
33
|
+
factset.to_hash
|
34
|
+
else
|
35
|
+
factset.value(names)
|
36
|
+
end
|
37
37
|
|
38
|
-
|
39
|
-
# reset the colleciton on each loop.
|
40
|
-
collection.flush
|
41
|
-
|
42
|
-
h[node.hostname] = node_facts
|
43
|
-
h
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
puts JSON.pretty_generate(facts)
|
38
|
+
puts JSON.pretty_generate(node_facts)
|
48
39
|
|
49
40
|
exit 0
|
50
41
|
end
|
data/lib/rfacter/config.rb
CHANGED
@@ -13,6 +13,7 @@ require_relative 'node'
|
|
13
13
|
# and contains methods for initializing the settings instance from
|
14
14
|
# various sources.
|
15
15
|
#
|
16
|
+
# @api public
|
16
17
|
# @since 0.1.0
|
17
18
|
module RFacter::Config
|
18
19
|
# Return global configuration
|
@@ -75,6 +76,10 @@ module RFacter::Config
|
|
75
76
|
settings.logger.level = Logger::DEBUG
|
76
77
|
end
|
77
78
|
|
79
|
+
parser.on('--profile', 'Profile fact resolution times.') do
|
80
|
+
settings.profile = true
|
81
|
+
end
|
82
|
+
|
78
83
|
parser.on('-n', '--node', '=MANDATORY', URI, 'Add a node by URI.') do |uri|
|
79
84
|
node = RFacter::Node.new(uri)
|
80
85
|
settings.nodes[node.hostname] = node
|
@@ -6,6 +6,7 @@ require_relative '../util/logger'
|
|
6
6
|
# Instances of this class hold top-level configuration values and shared
|
7
7
|
# service objects such as loggers.
|
8
8
|
#
|
9
|
+
# @api public
|
9
10
|
# @since 0.1.0
|
10
11
|
class RFacter::Config::Settings
|
11
12
|
# Access the logger instance
|
@@ -22,10 +23,16 @@ class RFacter::Config::Settings
|
|
22
23
|
# schemes to use when contacting them.
|
23
24
|
attr_reader :nodes
|
24
25
|
|
26
|
+
# A boolean switch for enabling execution profiling
|
27
|
+
#
|
28
|
+
# @return [Boolean] Defaults to false.
|
29
|
+
attr_accessor :profile
|
30
|
+
|
25
31
|
def initialize(**options)
|
26
32
|
@logger = RFacter::Util::Logger.new($stderr)
|
27
33
|
@logger.level = Logger::WARN
|
28
34
|
|
35
|
+
@profile = false
|
29
36
|
@nodes = Hash.new
|
30
37
|
end
|
31
38
|
end
|
@@ -8,12 +8,12 @@ require_relative '../config'
|
|
8
8
|
# Aggregates are evaluated in two parts: generating individual chunks and then
|
9
9
|
# aggregating all chunks together. Each chunk is a block of code that generates
|
10
10
|
# a value, and may depend on other chunks when it runs. After all chunks have
|
11
|
-
# been evaluated they are passed to the aggregate block as Hash<name, result
|
11
|
+
# been evaluated they are passed to the aggregate block as `Hash<name, result>`.
|
12
12
|
# The aggregate block converts the individual chunks into a single value that is
|
13
13
|
# returned as the final value of the aggregate.
|
14
14
|
#
|
15
|
-
# @api
|
16
|
-
# @since
|
15
|
+
# @api private
|
16
|
+
# @since 0.1.0
|
17
17
|
class RFacter::Core::Aggregate
|
18
18
|
require_relative 'directed_graph'
|
19
19
|
require_relative 'resolvable'
|
@@ -32,19 +32,17 @@ class RFacter::Core::Aggregate
|
|
32
32
|
attr_reader :name
|
33
33
|
|
34
34
|
# @!attribute [r] deps
|
35
|
-
# @
|
36
|
-
# @return [Facter::Core::DirectedGraph]
|
35
|
+
# @return [RFacter::Core::DirectedGraph]
|
37
36
|
attr_reader :deps
|
38
37
|
|
39
38
|
# @!attribute [r] confines
|
40
|
-
# @return [Array<
|
39
|
+
# @return [Array<RFacter::Core::Confine>] An array of confines restricting
|
41
40
|
# this to a specific platform
|
42
|
-
# @see
|
41
|
+
# @see RFacter::Core::Suitable
|
43
42
|
attr_reader :confines
|
44
43
|
|
45
44
|
# @!attribute [r] fact
|
46
|
-
# @return [
|
47
|
-
# @api private
|
45
|
+
# @return [RFacter::Util::Fact]
|
48
46
|
attr_reader :fact
|
49
47
|
|
50
48
|
def initialize(name, fact, config: RFacter::Config.config, **options)
|
@@ -83,8 +81,6 @@ class RFacter::Core::Aggregate
|
|
83
81
|
|
84
82
|
# Define a new chunk for the given aggregate
|
85
83
|
#
|
86
|
-
# @api public
|
87
|
-
#
|
88
84
|
# @example Defining a chunk with no dependencies
|
89
85
|
# aggregate.chunk(:mountpoints) do
|
90
86
|
# # generate mountpoint information
|
@@ -120,8 +116,6 @@ class RFacter::Core::Aggregate
|
|
120
116
|
|
121
117
|
# Define how all chunks should be combined
|
122
118
|
#
|
123
|
-
# @api public
|
124
|
-
#
|
125
119
|
# @example Merge all chunks
|
126
120
|
# aggregate.aggregate do |chunks|
|
127
121
|
# final_result = {}
|
@@ -160,7 +154,7 @@ class RFacter::Core::Aggregate
|
|
160
154
|
|
161
155
|
# Evaluate the results of this aggregate.
|
162
156
|
#
|
163
|
-
# @see
|
157
|
+
# @see RFacter::Core::Resolvable#value
|
164
158
|
# @return [Object]
|
165
159
|
def resolve_value
|
166
160
|
chunk_results = run_chunks()
|
@@ -9,6 +9,9 @@ require_relative '../util/normalization'
|
|
9
9
|
# Classes including this mixin should implement at #name method describing
|
10
10
|
# the value being resolved and a #resolve_value that actually executes the code
|
11
11
|
# to resolve the value.
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @since 0.1.0
|
12
15
|
module RFacter::Core::Resolvable
|
13
16
|
|
14
17
|
# The timeout, in seconds, for evaluating this resolution.
|
@@ -19,11 +22,11 @@ module RFacter::Core::Resolvable
|
|
19
22
|
# Return the timeout period for resolving a value.
|
20
23
|
# (see #timeout)
|
21
24
|
# @return [Numeric]
|
22
|
-
# @comment requiring 'timeout' stdlib class causes Object#timeout to be
|
23
|
-
# defined which delegates to Timeout.timeout. This method may potentially
|
24
|
-
# overwrite the #timeout attr_reader on this class, so we define #limit to
|
25
|
-
# avoid conflicts.
|
26
25
|
def limit
|
26
|
+
# requiring 'timeout' stdlib class causes Object#timeout to be defined
|
27
|
+
# which delegates to Timeout.timeout. This method may potentially overwrite
|
28
|
+
# the #timeout attr_reader on this class, so we define #limit to avoid
|
29
|
+
# conflicts.
|
27
30
|
@timeout || 0
|
28
31
|
end
|
29
32
|
|
@@ -39,8 +42,7 @@ module RFacter::Core::Resolvable
|
|
39
42
|
# Please see the Solaris zones fact for an example of how this feature may be
|
40
43
|
# used.
|
41
44
|
#
|
42
|
-
# @see
|
43
|
-
# @see Facter::Util::Resolution#flush
|
45
|
+
# @see RFacter::Util::Fact#flush
|
44
46
|
#
|
45
47
|
# @api public
|
46
48
|
def on_flush(&block)
|
@@ -48,10 +50,9 @@ module RFacter::Core::Resolvable
|
|
48
50
|
end
|
49
51
|
|
50
52
|
##
|
51
|
-
# flush executes the block, if any, stored by the {on_flush} method
|
53
|
+
# flush executes the block, if any, stored by the {#on_flush} method
|
52
54
|
#
|
53
|
-
# @see
|
54
|
-
# @see Facter::Util::Resolution#on_flush
|
55
|
+
# @see RFacter::Util::Fact#flush
|
55
56
|
#
|
56
57
|
# @api private
|
57
58
|
def flush
|
@@ -82,13 +83,16 @@ module RFacter::Core::Resolvable
|
|
82
83
|
private
|
83
84
|
|
84
85
|
def with_timing
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
unless @config.profile
|
87
|
+
yield
|
88
|
+
else
|
89
|
+
starttime = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
90
|
+
yield
|
91
|
+
finishtime = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
88
92
|
|
89
|
-
|
90
|
-
|
91
|
-
|
93
|
+
elapsed = (finishtime - starttime)
|
94
|
+
logger.info { "#{qualified_name}: #{"%.2f" % elapsed}ms" }
|
95
|
+
end
|
92
96
|
end
|
93
97
|
|
94
98
|
def qualified_name
|
@@ -4,7 +4,10 @@ require 'rfacter'
|
|
4
4
|
# certain platforms and determining the run precedence of these objects.
|
5
5
|
#
|
6
6
|
# Classes that include the Suitable mixin should define a `#confines` method
|
7
|
-
# that returns an Array of zero or more
|
7
|
+
# that returns an Array of zero or more {RFacter::Util::Confine} objects.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 0.1.0
|
8
11
|
module RFacter::Core::Suitable
|
9
12
|
require_relative '../util/confine'
|
10
13
|
|
@@ -18,8 +21,6 @@ module RFacter::Core::Suitable
|
|
18
21
|
# @param weight [Integer] the weight of this resolution
|
19
22
|
#
|
20
23
|
# @return [void]
|
21
|
-
#
|
22
|
-
# @api public
|
23
24
|
def has_weight(weight)
|
24
25
|
@weight = weight
|
25
26
|
end
|
@@ -29,8 +30,6 @@ module RFacter::Core::Suitable
|
|
29
30
|
#
|
30
31
|
# @return [void]
|
31
32
|
#
|
32
|
-
# @api public
|
33
|
-
#
|
34
33
|
# @overload confine(confines)
|
35
34
|
# Confine a fact to a specific fact value or values. This form takes a
|
36
35
|
# hash of fact names and values. Every fact must match the values given for
|
@@ -40,7 +39,7 @@ module RFacter::Core::Suitable
|
|
40
39
|
# @param [Hash{String,Symbol=>String,Array<String>}] confines set of facts identified by the hash keys whose
|
41
40
|
# fact value must match the argument value.
|
42
41
|
# @example Confining to Linux
|
43
|
-
#
|
42
|
+
# RFacter.add(:powerstates) do
|
44
43
|
# # This resolution only makes sense on linux systems
|
45
44
|
# confine :kernel => "Linux"
|
46
45
|
# setcode do
|
@@ -56,7 +55,7 @@ module RFacter::Core::Suitable
|
|
56
55
|
# @param [Proc] block determines the suitability of the fact. If the block
|
57
56
|
# evaluates to `false` or `nil` then the confined fact will not be
|
58
57
|
# evaluated.
|
59
|
-
# @yield [value] the value of the fact identified by
|
58
|
+
# @yield [value] the value of the fact identified by `confines`
|
60
59
|
# @example Confine the fact to a host with an ipaddress in a specific
|
61
60
|
# subnet
|
62
61
|
# confine :ipaddress do |addr|
|
@@ -95,8 +94,6 @@ module RFacter::Core::Suitable
|
|
95
94
|
# specific resolution wins over a less specific one).
|
96
95
|
#
|
97
96
|
# @return [Integer] the weight of this resolution
|
98
|
-
#
|
99
|
-
# @api private
|
100
97
|
def weight
|
101
98
|
if @weight
|
102
99
|
@weight
|
@@ -106,8 +103,6 @@ module RFacter::Core::Suitable
|
|
106
103
|
end
|
107
104
|
|
108
105
|
# Is this resolution mechanism suitable on the system in question?
|
109
|
-
#
|
110
|
-
# @api private
|
111
106
|
def suitable?
|
112
107
|
@confines.all? { |confine| confine.true? }
|
113
108
|
end
|
data/lib/rfacter/dsl.rb
CHANGED
@@ -7,9 +7,10 @@ require_relative 'util/non_nullable'
|
|
7
7
|
# Facter compatibility layer
|
8
8
|
#
|
9
9
|
# This module exists to provide compatibility shims for Facter DSL methods as
|
10
|
-
# exposed by the Facter 3
|
11
|
-
#
|
12
|
-
# these shims. The methods in this module should never be
|
10
|
+
# exposed by the Facter 3 Ruby Extension API. Any fact source code that is
|
11
|
+
# loaded into a {RFacter::Util::Collection} instance via `instance_eval`
|
12
|
+
# should pick up on these shims. The methods in this module should never be
|
13
|
+
# called outside of files that define custom facts.
|
13
14
|
#
|
14
15
|
# However, lexical scope is a tricky thing, so "should" is the operative word
|
15
16
|
# here.
|
@@ -60,7 +61,7 @@ EOS
|
|
60
61
|
# of {RFacter::Util::Fact} and {Facter::Util::Resolution} can be
|
61
62
|
# supplied here
|
62
63
|
# @option options [Integer] :timeout set the
|
63
|
-
# {RFacter::
|
64
|
+
# {RFacter::Core::Resolvable#timeout timeout} for this resolution
|
64
65
|
# @param block [Proc] a block defining a fact resolution
|
65
66
|
#
|
66
67
|
# @return [RFacter::Util::Fact] the fact object, which includes any previously
|
@@ -104,7 +105,7 @@ EOS
|
|
104
105
|
#
|
105
106
|
# @return [RFacter::Util::Fact] The fact that was defined
|
106
107
|
#
|
107
|
-
# @see
|
108
|
+
# @see RFacter::Util::Collection#define_fact
|
108
109
|
def self.define_fact(name, options = {}, &block)
|
109
110
|
COLLECTION.value.define_fact(name, options, &block)
|
110
111
|
end
|
@@ -116,7 +117,7 @@ EOS
|
|
116
117
|
#
|
117
118
|
# @return [void]
|
118
119
|
def self.each
|
119
|
-
COLLECTION.value.each
|
120
|
+
COLLECTION.value.each do |*args|
|
120
121
|
yield(*args)
|
121
122
|
end
|
122
123
|
end
|
@@ -185,7 +186,7 @@ EOS
|
|
185
186
|
#
|
186
187
|
# @return [Hash{String => Object}] the hash of fact names and values
|
187
188
|
def self.to_hash
|
188
|
-
COLLECTION.value.to_hash
|
189
|
+
COLLECTION.value.to_hash
|
189
190
|
end
|
190
191
|
|
191
192
|
# Gets the value for a fact.
|
@@ -195,7 +196,7 @@ EOS
|
|
195
196
|
# @return [Object, nil] the value of the fact, or nil if no fact is
|
196
197
|
# found
|
197
198
|
def self.value(name)
|
198
|
-
COLLECTION.value.value(name
|
199
|
+
COLLECTION.value.value(name)
|
199
200
|
end
|
200
201
|
|
201
202
|
# Returns the current RFacter version
|
@@ -304,11 +305,11 @@ EOS
|
|
304
305
|
# Facter::Util DSL methods
|
305
306
|
module Util
|
306
307
|
require_relative 'util/fact'
|
307
|
-
#
|
308
|
+
# @see RFacter::Util::Fact
|
308
309
|
Fact = ::RFacter::Util::Fact
|
309
310
|
|
310
311
|
require_relative 'util/resolution'
|
311
|
-
#
|
312
|
+
# @see RFacter::Util::Resolution
|
312
313
|
Resolution = ::RFacter::Util::Resolution
|
313
314
|
|
314
315
|
# Methods for interacting with remote files.
|
data/lib/rfacter/facts/os.rb
CHANGED
@@ -40,21 +40,56 @@ Facter.add(:os, :type => :aggregate) do
|
|
40
40
|
# stats and reads, so multiple file ops only incur a cost once. However,
|
41
41
|
# these ops are all going over the network, so we should probably
|
42
42
|
# prioritize RedHat and SuSE detection over less common Linuxen in order to
|
43
|
-
# cut down on network chatter that will be useless in most cases.
|
44
|
-
# might be worth adding /etc/os-release detection at the front of the list
|
45
|
-
# since that's the new hotness from systemd standardization.
|
43
|
+
# cut down on network chatter that will be useless in most cases.
|
46
44
|
operatingsystem = nil
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
46
|
+
# Determine OS name from /etc/os-release if it exists.
|
47
|
+
#
|
48
|
+
# see: https://www.freedesktop.org/software/systemd/man/os-release.html
|
49
|
+
if Facter::Util::FileRead.exists?('/etc/os-release')
|
50
|
+
os_release = Facter::Util::FileRead.read('/etc/os-release')
|
51
|
+
# NOTE: The ID field is used instead of NAME as ID was explicitly
|
52
|
+
# designed to be parsed by scripts whereas NAME is for human consumption.
|
53
|
+
os_id = os_release.lines.find {|l| l.start_with?('ID=')}
|
54
|
+
|
55
|
+
unless os_id.nil?
|
56
|
+
operatingsystem = case os_id
|
57
|
+
when /debian/i
|
58
|
+
'Debian'
|
59
|
+
when /ubuntu/i
|
60
|
+
'Ubuntu'
|
61
|
+
when /opensuse/i
|
62
|
+
'OpenSuSE'
|
63
|
+
when /sles/i
|
64
|
+
'SLES'
|
65
|
+
when /fedora/i
|
66
|
+
'Fedora'
|
67
|
+
when /centos/i
|
68
|
+
'CentOS'
|
69
|
+
when /ol/i
|
70
|
+
'OracleLinux'
|
71
|
+
when /rhel/i
|
72
|
+
'RedHat'
|
73
|
+
when /amzn/i
|
74
|
+
'Amazon'
|
75
|
+
else
|
76
|
+
os_id.scan(/^(?:\w+)=[\"']?(.+?)[\"']?$/).flatten.first
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if operatingsystem.nil?
|
82
|
+
operatingsystem = if Facter.value(:kernel) == "GNU/kFreeBSD"
|
83
|
+
"GNU/kFreeBSD"
|
84
|
+
elsif Facter::Util::FileRead.exists?('/etc/debian_version')
|
85
|
+
case Facter::Core::Execution.exec('lsb_release -i')
|
86
|
+
when /Ubuntu/i
|
87
|
+
'Ubuntu'
|
88
|
+
when /LinuxMint/i
|
89
|
+
'LinuxMint'
|
90
|
+
else
|
91
|
+
'Debian'
|
92
|
+
end
|
58
93
|
end
|
59
94
|
end
|
60
95
|
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'rfacter'
|
4
|
+
|
5
|
+
require_relative 'config'
|
6
|
+
require_relative 'node'
|
7
|
+
require_relative 'util/collection'
|
8
|
+
|
9
|
+
# A class that can retrieve facts from several nodes
|
10
|
+
#
|
11
|
+
# A factset joins instances of {RFacter::Node} to {RFacter::Util::Collection}
|
12
|
+
# and enables parallel and asynchronous resolution of fact values across
|
13
|
+
# several nodes. Supports retrieving single facts asynchronously via {#fetch}
|
14
|
+
# and in a blocking fashion via {#value}. All facts can be retrieved
|
15
|
+
# asynchronously via {#fetch_all} and in a blocking fashion via {#to_hash}.
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
# @since 0.1.0
|
19
|
+
class RFacter::Factset
|
20
|
+
extend Forwardable
|
21
|
+
|
22
|
+
instance_delegate([:logger] => :@config)
|
23
|
+
|
24
|
+
# Returns a new instance of Factset
|
25
|
+
#
|
26
|
+
# @param nodes [Array<RFacter::Node>] An array of node objects to collect
|
27
|
+
# facts from.
|
28
|
+
def initialize(nodes, config: RFacter::Config.config, **opts)
|
29
|
+
@config = config
|
30
|
+
|
31
|
+
@collections = nodes.each_with_object({}) do |node, hash|
|
32
|
+
hash[node.id] = RFacter::Util::Collection.new(node)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Asynchronously fetch the value of a fact from each node
|
37
|
+
#
|
38
|
+
# This method spawns a background thread per node which resolves the value
|
39
|
+
# of a fact specified by `query`.
|
40
|
+
#
|
41
|
+
# @param queries [Array<String>] The names of the facts to fetch.
|
42
|
+
#
|
43
|
+
# @return [Concurrent::Future{String => Hash}]
|
44
|
+
# A future that will return a hash mapping the node id to a hash containing
|
45
|
+
# the resolved facts when `value` is called.
|
46
|
+
def fetch(queries)
|
47
|
+
queries = Array(queries)
|
48
|
+
# Spawn async lookups in the background for each node.
|
49
|
+
futures = @collections.each_with_object({}) do |(name, collection), hash|
|
50
|
+
hash[name] = {}
|
51
|
+
queries.each do |query|
|
52
|
+
hash[name][query] = collection.async.value(query)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return a future with the resolved values.
|
57
|
+
Concurrent::Future.execute do
|
58
|
+
futures.each_with_object({}) do |(name, ivars), hash|
|
59
|
+
hash[name] = ivars.each_with_object({}) do |(query, ivar), results|
|
60
|
+
# TODO: Add exception handling for failed futures.
|
61
|
+
results[query] = ivar.value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Fetch the value of a fact from each node
|
68
|
+
#
|
69
|
+
# This method calls {#fetch} and then blocks until the result is available.
|
70
|
+
#
|
71
|
+
# @return [Hash{String => Hash}]
|
72
|
+
# A hash mapping the node id to a hash containing the resolved facts.
|
73
|
+
def value(queries)
|
74
|
+
fetch(queries).value
|
75
|
+
end
|
76
|
+
|
77
|
+
# Asynchronously fetch all facts from each node
|
78
|
+
#
|
79
|
+
# This method spawns a background thread per node which resolves all
|
80
|
+
# fact values for each node.
|
81
|
+
#
|
82
|
+
# @return [Concurrent::Future{String => Hash}]
|
83
|
+
# A future that will return a hash mapping the node id to a hash containing
|
84
|
+
# the resolved facts when `value` is called.
|
85
|
+
def fetch_all
|
86
|
+
futures = @collections.each_with_object({}) do |(name, collection), hash|
|
87
|
+
collection.async.load_all
|
88
|
+
hash[name] = collection.async.to_hash
|
89
|
+
end
|
90
|
+
|
91
|
+
Concurrent::Future.execute do
|
92
|
+
futures.each_with_object({}) do |(name, future), hash|
|
93
|
+
# TODO: Add exception handling for failed futures.
|
94
|
+
hash[name] = future.value
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Fetch all facts from each node
|
100
|
+
#
|
101
|
+
# This method calls {#fetch_all} and then blocks until the result
|
102
|
+
# is available.
|
103
|
+
#
|
104
|
+
# @return [Hash{String => Hash}]
|
105
|
+
# A hash mapping the node id to a hash containing the resolved facts.
|
106
|
+
def to_hash
|
107
|
+
fetch_all.value
|
108
|
+
end
|
109
|
+
|
110
|
+
# Flush cached fact values
|
111
|
+
#
|
112
|
+
# @return [void]
|
113
|
+
def flush
|
114
|
+
@collections.values.each {|c| c.flush}
|
115
|
+
end
|
116
|
+
end
|
data/lib/rfacter/node.rb
CHANGED
@@ -13,6 +13,7 @@ require 'concurrent'
|
|
13
13
|
# @note This class should be refacter to provide an abstracted interface to
|
14
14
|
# different transport backends like Train, Vagrant, Chloride, etc.
|
15
15
|
#
|
16
|
+
# @api public
|
16
17
|
# @since 0.1.0
|
17
18
|
class RFacter::Node
|
18
19
|
extend Forwardable
|
@@ -34,10 +35,17 @@ class RFacter::Node
|
|
34
35
|
attr_reader :password
|
35
36
|
# @return [Hash]
|
36
37
|
attr_reader :options
|
38
|
+
# @return [String]
|
39
|
+
attr_reader :id
|
37
40
|
|
38
41
|
attr_reader :transport
|
39
42
|
|
40
|
-
|
43
|
+
# Returns a new instance of Node
|
44
|
+
#
|
45
|
+
# @param uri [URI] The URI of the node.
|
46
|
+
# @param id [String, nil] An optional string to use when identifying
|
47
|
+
# this node.
|
48
|
+
def initialize(uri, id: nil, config: RFacter::Config.config, **opts)
|
41
49
|
@config = config
|
42
50
|
|
43
51
|
@uri = unless uri.is_a?(URI)
|
@@ -67,6 +75,20 @@ class RFacter::Node
|
|
67
75
|
@options = @uri.query.nil? ? Hash.new : CGI.parse(@uri.query)
|
68
76
|
@options.update(opts)
|
69
77
|
|
78
|
+
@id = unless id.nil?
|
79
|
+
id
|
80
|
+
else
|
81
|
+
# Create a default from the URI, minus the password and options
|
82
|
+
# components.
|
83
|
+
id_string = "#{@scheme}://"
|
84
|
+
id_string += "#{@user}@" unless @user.nil?
|
85
|
+
id_string += @hostname
|
86
|
+
id_string += ":#{@port}" unless @port.nil?
|
87
|
+
id_string
|
88
|
+
end
|
89
|
+
|
90
|
+
@id.freeze
|
91
|
+
|
70
92
|
# TODO: This should be abstracted.
|
71
93
|
@transport = Train.create(@scheme,
|
72
94
|
host: @hostname,
|
@@ -135,3 +157,39 @@ class RFacter::Node
|
|
135
157
|
connection.file(path)
|
136
158
|
end
|
137
159
|
end
|
160
|
+
|
161
|
+
|
162
|
+
# Hello dear reader. Hiding at the bottom of this file is a consequence of bad
|
163
|
+
# design decisions in Ruby --- specifically the Kernel.autoload feature. Train
|
164
|
+
# uses autload to lazily require its subcomponents. However, autoload plays
|
165
|
+
# filthy tricks with the resolution of Constants that are not thread-safe. So,
|
166
|
+
# this chunk of code is sitting down here out of sight to force these autoloads
|
167
|
+
# to be resolved up front so that we can get on with the business of actually
|
168
|
+
# saving significant time by using parallelism.
|
169
|
+
#
|
170
|
+
# See:
|
171
|
+
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/41149
|
172
|
+
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/20238
|
173
|
+
#
|
174
|
+
# TODO: Remove if this PR lands:
|
175
|
+
# https://github.com/chef/train/pull/178
|
176
|
+
require 'train/plugins/transport'
|
177
|
+
require 'train/plugins/base_connection'
|
178
|
+
|
179
|
+
require 'train/transports/ssh'
|
180
|
+
require 'train/transports/ssh_connection'
|
181
|
+
require 'train/transports/winrm'
|
182
|
+
require 'train/transports/winrm_connection'
|
183
|
+
require 'train/transports/local'
|
184
|
+
require 'train/transports/local_file'
|
185
|
+
require 'train/transports/local_os'
|
186
|
+
|
187
|
+
require 'train/extras'
|
188
|
+
require 'train/extras/command_wrapper'
|
189
|
+
require 'train/extras/file_common'
|
190
|
+
require 'train/extras/file_unix'
|
191
|
+
require 'train/extras/file_aix'
|
192
|
+
require 'train/extras/file_linux'
|
193
|
+
require 'train/extras/file_windows'
|
194
|
+
require 'train/extras/os_common'
|
195
|
+
require 'train/extras/stat'
|
@@ -1,16 +1,22 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
|
+
require 'concurrent'
|
4
|
+
|
3
5
|
require 'rfacter'
|
4
6
|
require_relative '../config'
|
5
7
|
require_relative '../dsl'
|
6
8
|
require_relative 'loader'
|
7
9
|
require_relative 'fact'
|
8
10
|
|
9
|
-
# Manage which facts exist and how we access them.
|
10
|
-
#
|
11
|
+
# Manage which facts exist on a Node and how we access them.
|
12
|
+
#
|
13
|
+
# Largely just a wrapper around a hash of facts that have been retrieved from a
|
14
|
+
# particular node.
|
11
15
|
#
|
12
16
|
# @api private
|
17
|
+
# @since 0.1.0
|
13
18
|
class RFacter::Util::Collection
|
19
|
+
include Concurrent::Async
|
14
20
|
# Ensures unqualified namespaces like `Facter` and `Facter::Util` get
|
15
21
|
# re-directed to RFacter shims when the loader calls `instance_eval`
|
16
22
|
include RFacter::DSL
|
@@ -18,15 +24,21 @@ class RFacter::Util::Collection
|
|
18
24
|
|
19
25
|
instance_delegate([:logger] => :@config)
|
20
26
|
|
21
|
-
|
27
|
+
# Initialize a new Collection object
|
28
|
+
#
|
29
|
+
# @param node [RFacter::Node] The node from which this collection
|
30
|
+
# should retrieve facts.
|
31
|
+
def initialize(node, config: RFacter::Config.config, **opts)
|
32
|
+
@node = node
|
22
33
|
@config = config
|
34
|
+
|
23
35
|
@facts = Hash.new
|
24
36
|
@internal_loader = RFacter::Util::Loader.new
|
25
37
|
end
|
26
38
|
|
27
39
|
# Return a fact object by name.
|
28
|
-
def [](name
|
29
|
-
value(name
|
40
|
+
def [](name)
|
41
|
+
value(name)
|
30
42
|
end
|
31
43
|
|
32
44
|
# Define a new fact or extend an existing fact.
|
@@ -34,7 +46,7 @@ class RFacter::Util::Collection
|
|
34
46
|
# @param name [Symbol] The name of the fact to define
|
35
47
|
# @param options [Hash] A hash of options to set on the fact
|
36
48
|
#
|
37
|
-
# @return [
|
49
|
+
# @return [RFacter::Util::Fact] The fact that was defined
|
38
50
|
def define_fact(name, options = {}, &block)
|
39
51
|
fact = create_or_return_fact(name, options)
|
40
52
|
|
@@ -53,7 +65,7 @@ class RFacter::Util::Collection
|
|
53
65
|
# @param name [Symbol] The name of the fact to define
|
54
66
|
# @param options [Hash] A hash of options to set on the fact and resolution
|
55
67
|
#
|
56
|
-
# @return [
|
68
|
+
# @return [RFacter::Util::Fact] The fact that was defined
|
57
69
|
def add(name, options = {}, &block)
|
58
70
|
fact = create_or_return_fact(name, options)
|
59
71
|
|
@@ -65,11 +77,11 @@ class RFacter::Util::Collection
|
|
65
77
|
include Enumerable
|
66
78
|
|
67
79
|
# Iterate across all of the facts.
|
68
|
-
def each
|
80
|
+
def each
|
69
81
|
load_all
|
70
82
|
|
71
|
-
|
72
|
-
|
83
|
+
COLLECTION.bind(self) do
|
84
|
+
NODE.bind(@node) do
|
73
85
|
@facts.each do |name, fact|
|
74
86
|
value = fact.value
|
75
87
|
unless value.nil?
|
@@ -118,24 +130,22 @@ class RFacter::Util::Collection
|
|
118
130
|
end
|
119
131
|
|
120
132
|
# Return a hash of all of our facts.
|
121
|
-
def to_hash
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
133
|
+
def to_hash
|
134
|
+
COLLECTION.bind(self) do
|
135
|
+
NODE.bind(@node) do
|
136
|
+
@facts.each_with_object({}) do |(name, fact), hash|
|
137
|
+
resolved_value = fact.value
|
138
|
+
|
139
|
+
# For backwards compatibility, convert the fact name to a string.
|
140
|
+
hash[name.to_s] = resolved_value unless resolved_value.nil?
|
126
141
|
end
|
127
142
|
end
|
128
|
-
|
129
|
-
# For backwards compatibility, convert the fact name to a string.
|
130
|
-
h[ary[0].to_s] = resolved_value unless resolved_value.nil?
|
131
|
-
|
132
|
-
h
|
133
143
|
end
|
134
144
|
end
|
135
145
|
|
136
|
-
def value(name
|
137
|
-
|
138
|
-
|
146
|
+
def value(name)
|
147
|
+
COLLECTION.bind(self) do
|
148
|
+
NODE.bind(@node) do
|
139
149
|
if fact = fact(name)
|
140
150
|
fact.value
|
141
151
|
end
|
@@ -153,8 +163,6 @@ class RFacter::Util::Collection
|
|
153
163
|
if fact.nil?
|
154
164
|
fact = RFacter::Util::Fact.new(name, options)
|
155
165
|
@facts[name] = fact
|
156
|
-
else
|
157
|
-
fact.extract_ldapname_option!(options)
|
158
166
|
end
|
159
167
|
|
160
168
|
fact
|
data/lib/rfacter/util/confine.rb
CHANGED
data/lib/rfacter/util/fact.rb
CHANGED
@@ -4,11 +4,12 @@ require 'rfacter'
|
|
4
4
|
require_relative '../config'
|
5
5
|
|
6
6
|
# This class represents a fact. Each fact has a name and multiple
|
7
|
-
# {
|
7
|
+
# {RFacter::Util::Resolution resolutions}.
|
8
8
|
#
|
9
|
-
# Create facts using {Facter.add}
|
9
|
+
# Create facts using {RFacter::DSL::Facter.add Facter.add}
|
10
10
|
#
|
11
|
-
# @api
|
11
|
+
# @api private
|
12
|
+
# @since 0.1.0
|
12
13
|
class RFacter::Util::Fact
|
13
14
|
require_relative '../core/aggregate'
|
14
15
|
require_relative 'resolution'
|
@@ -21,40 +22,27 @@ class RFacter::Util::Fact
|
|
21
22
|
# @return [String]
|
22
23
|
attr_accessor :name
|
23
24
|
|
24
|
-
#
|
25
|
-
# @deprecated
|
26
|
-
attr_accessor :ldapname
|
27
|
-
|
28
|
-
# Creates a new fact, with no resolution mechanisms. See {Facter.add}
|
25
|
+
# Creates a new fact, with no resolution mechanisms. See {RFacter::DSL::Facter.add}
|
29
26
|
# for the public API for creating facts.
|
30
27
|
# @param name [String] the fact name
|
31
28
|
# @param options [Hash] optional parameters
|
32
|
-
# @option options [String] :ldapname set the ldapname property on the fact
|
33
|
-
#
|
34
|
-
# @api private
|
35
29
|
def initialize(name, config: RFacter::Config.config, **options)
|
36
30
|
@name = name.to_s.downcase.intern
|
37
31
|
@config = config
|
38
32
|
|
39
|
-
extract_ldapname_option!(options)
|
40
|
-
|
41
|
-
@ldapname ||= @name.to_s
|
42
|
-
|
43
33
|
@resolves = []
|
44
34
|
@searching = false
|
45
35
|
|
46
36
|
@value = nil
|
47
37
|
end
|
48
38
|
|
49
|
-
# Adds a new {
|
39
|
+
# Adds a new {RFacter::Util::Resolution resolution}. This requires a
|
50
40
|
# block, which will then be evaluated in the context of the new
|
51
41
|
# resolution.
|
52
42
|
#
|
53
43
|
# @param options [Hash] A hash of options to set on the resolution
|
54
44
|
#
|
55
|
-
# @return [
|
56
|
-
#
|
57
|
-
# @api private
|
45
|
+
# @return [RFacter::Util::Resolution]
|
58
46
|
def add(options = {}, &block)
|
59
47
|
define_resolution(nil, options, &block)
|
60
48
|
end
|
@@ -64,9 +52,7 @@ class RFacter::Util::Fact
|
|
64
52
|
#
|
65
53
|
# @param resolution_name [String] The name of the resolve to define or look up
|
66
54
|
# @param options [Hash] A hash of options to set on the resolution
|
67
|
-
# @return [
|
68
|
-
#
|
69
|
-
# @api public
|
55
|
+
# @return [RFacter::Util::Resolution]
|
70
56
|
def define_resolution(resolution_name, options = {}, &block)
|
71
57
|
|
72
58
|
resolution_type = options.delete(:type) || :simple
|
@@ -85,7 +71,7 @@ class RFacter::Util::Fact
|
|
85
71
|
#
|
86
72
|
# @param name [String]
|
87
73
|
#
|
88
|
-
# @return [
|
74
|
+
# @return [RFacter::Util::Resolution, nil] The resolution if exists, nil if
|
89
75
|
# it doesn't exist or name is nil
|
90
76
|
def resolution(name)
|
91
77
|
return nil if name.nil?
|
@@ -96,18 +82,14 @@ class RFacter::Util::Fact
|
|
96
82
|
# Flushes any cached values.
|
97
83
|
#
|
98
84
|
# @return [void]
|
99
|
-
#
|
100
|
-
# @api private
|
101
85
|
def flush
|
102
86
|
@resolves.each { |r| r.flush }
|
103
87
|
@value = nil
|
104
88
|
end
|
105
89
|
|
106
90
|
# Returns the value for this fact. This searches all resolutions by
|
107
|
-
# suitability and weight (see {
|
91
|
+
# suitability and weight (see {RFacter::Util::Resolution}). If no
|
108
92
|
# suitable resolution is found, it returns nil.
|
109
|
-
#
|
110
|
-
# @api public
|
111
93
|
def value
|
112
94
|
return @value if @value
|
113
95
|
|
@@ -128,15 +110,6 @@ class RFacter::Util::Fact
|
|
128
110
|
end
|
129
111
|
end
|
130
112
|
|
131
|
-
# @api private
|
132
|
-
# @deprecated
|
133
|
-
def extract_ldapname_option!(options)
|
134
|
-
if options[:ldapname]
|
135
|
-
logger.warnonce("ldapname is deprecated and will be removed in a future version")
|
136
|
-
self.ldapname = options.delete(:ldapname)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
113
|
private
|
141
114
|
|
142
115
|
# Are we in the midst of a search?
|
data/lib/rfacter/util/loader.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative '../dsl'
|
|
8
8
|
# Load facts on demand.
|
9
9
|
#
|
10
10
|
# @api private
|
11
|
+
# @since 0.1.0
|
11
12
|
class RFacter::Util::Loader
|
12
13
|
extend Forwardable
|
13
14
|
|
@@ -20,7 +21,6 @@ class RFacter::Util::Loader
|
|
20
21
|
|
21
22
|
# Load all resolutions for a single fact.
|
22
23
|
#
|
23
|
-
# @api public
|
24
24
|
# @param fact [Symbol]
|
25
25
|
def load(fact, collection)
|
26
26
|
# Now load from the search path
|
@@ -40,8 +40,6 @@ class RFacter::Util::Loader
|
|
40
40
|
end
|
41
41
|
|
42
42
|
# Load all facts from all directories.
|
43
|
-
#
|
44
|
-
# @api public
|
45
43
|
def load_all(collection)
|
46
44
|
return if defined?(@loaded_all)
|
47
45
|
|
@@ -69,7 +67,6 @@ class RFacter::Util::Loader
|
|
69
67
|
# A warning will be generated for paths that are not
|
70
68
|
# absolute directories.
|
71
69
|
#
|
72
|
-
# @api public
|
73
70
|
# @return [Array<String>]
|
74
71
|
def search_path
|
75
72
|
search_paths = [File.expand_path('../../facts', __FILE__)]
|
data/lib/rfacter/util/logger.rb
CHANGED
@@ -8,6 +8,7 @@ require 'rfacter'
|
|
8
8
|
# if de-referenced to `nil`. This allows the creation of variables
|
9
9
|
# that must always be bound to a specific value before use.
|
10
10
|
#
|
11
|
+
# @api private
|
11
12
|
# @since 0.1.0
|
12
13
|
class RFacter::Util::NonNullable < Concurrent::ThreadLocalVar
|
13
14
|
# @param err_message [String] The error message to raise if
|
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'rfacter'
|
2
2
|
|
3
|
+
# Routines for normalizing fact return values
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @since 0.1.0
|
3
7
|
module RFacter
|
4
8
|
module Util
|
5
9
|
module Normalization
|
@@ -11,7 +15,6 @@ module RFacter
|
|
11
15
|
|
12
16
|
# Recursively normalize the given data structure
|
13
17
|
#
|
14
|
-
# @api public
|
15
18
|
# @raise [NormalizationError] If the data structure contained an invalid element.
|
16
19
|
# @return [void]
|
17
20
|
def normalize(value)
|
@@ -33,46 +36,27 @@ module RFacter
|
|
33
36
|
#
|
34
37
|
# Attempt to normalize and validate the given string.
|
35
38
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
+
# The string is validate by checking that the string encoding is UTF-8
|
40
|
+
# and that the string content matches the encoding. If the string is not
|
41
|
+
# an expected encoding then it is converted to UTF-8.
|
39
42
|
#
|
40
|
-
# On Ruby 1.9+, the string is validate by checking that the string encoding
|
41
|
-
# is UTF-8 and that the string content matches the encoding. If the string
|
42
|
-
# is not an expected encoding then it is converted to UTF-8.
|
43
|
-
#
|
44
|
-
# @api public
|
45
43
|
# @raise [NormalizationError] If the string used an unsupported encoding or did not match its encoding
|
46
44
|
# @param value [String]
|
47
45
|
# @return [void]
|
46
|
+
def normalize_string(value)
|
47
|
+
value = value.encode(Encoding::UTF_8)
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def normalize_string(value)
|
53
|
-
converted = Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
|
54
|
-
if converted != value
|
55
|
-
raise NormalizationError, "String #{value.inspect} is not valid UTF-8"
|
56
|
-
end
|
57
|
-
value
|
49
|
+
unless value.valid_encoding?
|
50
|
+
raise NormalizationError, "String #{value.inspect} doesn't match the reported encoding #{value.encoding}"
|
58
51
|
end
|
59
|
-
else
|
60
|
-
def normalize_string(value)
|
61
|
-
value = value.encode(Encoding::UTF_8)
|
62
52
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
value
|
68
|
-
rescue EncodingError
|
69
|
-
raise NormalizationError, "String encoding #{value.encoding} is not UTF-8 and could not be converted to UTF-8"
|
70
|
-
end
|
53
|
+
value
|
54
|
+
rescue EncodingError
|
55
|
+
raise NormalizationError, "String encoding #{value.encoding} is not UTF-8 and could not be converted to UTF-8"
|
71
56
|
end
|
72
57
|
|
73
58
|
# Validate all elements of the array.
|
74
59
|
#
|
75
|
-
# @api public
|
76
60
|
# @raise [NormalizationError] If one of the elements failed validation
|
77
61
|
# @param value [Array]
|
78
62
|
# @return [void]
|
@@ -84,7 +68,6 @@ module RFacter
|
|
84
68
|
|
85
69
|
# Validate all keys and values of the hash.
|
86
70
|
#
|
87
|
-
# @api public
|
88
71
|
# @raise [NormalizationError] If one of the keys or values failed normalization
|
89
72
|
# @param value [Hash]
|
90
73
|
# @return [void]
|
@@ -6,14 +6,15 @@ require_relative '../config'
|
|
6
6
|
# This represents a fact resolution. A resolution is a concrete
|
7
7
|
# implementation of a fact. A single fact can have many resolutions and
|
8
8
|
# the correct resolution will be chosen at runtime. Each time
|
9
|
-
# {Facter.add} is called, a new resolution is created
|
10
|
-
# set of resolutions for the fact named in the call. Each
|
11
|
-
# has a {#has_weight weight}, which defines its priority over other
|
9
|
+
# {RFacter::DSL::Facter.add Facter.add} is called, a new resolution is created
|
10
|
+
# and added to the set of resolutions for the fact named in the call. Each
|
11
|
+
# resolution has a {#has_weight weight}, which defines its priority over other
|
12
12
|
# resolutions, and a set of {#confine _confinements_}, which defines the
|
13
|
-
# conditions under which it will be chosen. All confinements must be
|
14
|
-
#
|
13
|
+
# conditions under which it will be chosen. All confinements must be satisfied
|
14
|
+
# for a fact to be considered _suitable_.
|
15
15
|
#
|
16
|
-
# @api
|
16
|
+
# @api private
|
17
|
+
# @since 0.1.0
|
17
18
|
class RFacter::Util::Resolution
|
18
19
|
require_relative '../dsl'
|
19
20
|
require_relative '../core/resolvable'
|
@@ -22,7 +23,6 @@ class RFacter::Util::Resolution
|
|
22
23
|
|
23
24
|
instance_delegate([:logger] => :@config)
|
24
25
|
|
25
|
-
# @api private
|
26
26
|
attr_accessor :code
|
27
27
|
attr_writer :value
|
28
28
|
|
@@ -33,12 +33,10 @@ class RFacter::Util::Resolution
|
|
33
33
|
# The name of this resolution. The resolution name should be unique with
|
34
34
|
# respect to the given fact.
|
35
35
|
# @return [String]
|
36
|
-
# @api public
|
37
36
|
attr_accessor :name
|
38
37
|
|
39
38
|
# @!attribute [r] fact
|
40
|
-
# @return [
|
41
|
-
# @api private
|
39
|
+
# @return [RFacter::Util::Fact]
|
42
40
|
attr_reader :fact
|
43
41
|
|
44
42
|
def which(command)
|
@@ -53,8 +51,6 @@ class RFacter::Util::Resolution
|
|
53
51
|
#
|
54
52
|
# @param name [String] The name of the resolution.
|
55
53
|
# @return [void]
|
56
|
-
#
|
57
|
-
# @api private
|
58
54
|
def initialize(name, fact, config: RFacter::Config.config, **options)
|
59
55
|
@name = name
|
60
56
|
@fact = fact
|
@@ -83,14 +79,7 @@ class RFacter::Util::Resolution
|
|
83
79
|
|
84
80
|
instance_eval(&block)
|
85
81
|
|
86
|
-
|
87
|
-
# debugging information if a resolution is being evaluated twice. Since 1.8
|
88
|
-
# doesn't support this we opportunistically provide this information.
|
89
|
-
if block.respond_to? :source_location
|
90
|
-
@last_evaluated = block.source_location.join(':')
|
91
|
-
else
|
92
|
-
@last_evaluated = true
|
93
|
-
end
|
82
|
+
@last_evaluated = block.source_location.join(':')
|
94
83
|
end
|
95
84
|
|
96
85
|
def set_options(options)
|
@@ -130,8 +119,6 @@ class RFacter::Util::Resolution
|
|
130
119
|
# @param [Proc] block The block to determine the resolution's value.
|
131
120
|
# This block is run when the fact is evaluated. Errors raised from
|
132
121
|
# inside the block are rescued and printed to stderr.
|
133
|
-
#
|
134
|
-
# @api public
|
135
122
|
def setcode(string = nil, &block)
|
136
123
|
if string
|
137
124
|
@code = Proc.new do
|
data/lib/rfacter/util/values.rb
CHANGED
data/lib/rfacter/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rfacter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Sharpsteen
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-05-
|
12
|
+
date: 2017-05-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: train
|
@@ -40,19 +40,19 @@ dependencies:
|
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '1.0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
43
|
+
name: rake
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0
|
48
|
+
version: '12.0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0
|
55
|
+
version: '12.0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: rspec
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,6 +67,20 @@ dependencies:
|
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '3.1'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: yard
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.9'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.9'
|
70
84
|
description: |
|
71
85
|
RFacter is a library for collecting facts from remote system(s) by executing
|
72
86
|
commands over transports such as SSH and WinRM.
|
@@ -92,6 +106,7 @@ files:
|
|
92
106
|
- lib/rfacter/facts/kernelversion.rb
|
93
107
|
- lib/rfacter/facts/networking.rb
|
94
108
|
- lib/rfacter/facts/os.rb
|
109
|
+
- lib/rfacter/factset.rb
|
95
110
|
- lib/rfacter/node.rb
|
96
111
|
- lib/rfacter/util/collection.rb
|
97
112
|
- lib/rfacter/util/confine.rb
|