bolt 2.24.1 → 2.29.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +1 -1
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +1 -1
- data/lib/bolt/analytics.rb +7 -3
- data/lib/bolt/applicator.rb +21 -21
- data/lib/bolt/bolt_option_parser.rb +77 -26
- data/lib/bolt/catalog.rb +4 -2
- data/lib/bolt/cli.rb +135 -147
- data/lib/bolt/config.rb +48 -25
- data/lib/bolt/config/options.rb +34 -2
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +8 -1
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/inventory/inventory.rb +1 -1
- data/lib/bolt/inventory/target.rb +1 -1
- data/lib/bolt/logger.rb +35 -21
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/pal.rb +21 -10
- data/lib/bolt/pal/yaml_plan/evaluator.rb +1 -1
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +62 -17
- data/lib/bolt/puppetdb/client.rb +1 -1
- data/lib/bolt/puppetdb/config.rb +1 -1
- data/lib/bolt/puppetfile.rb +160 -0
- data/lib/bolt/puppetfile/installer.rb +43 -0
- data/lib/bolt/puppetfile/module.rb +89 -0
- data/lib/bolt/r10k_log_proxy.rb +1 -1
- data/lib/bolt/rerun.rb +2 -2
- data/lib/bolt/result.rb +23 -0
- data/lib/bolt/shell.rb +1 -1
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/base.rb +5 -5
- data/lib/bolt/transport/docker/connection.rb +1 -1
- data/lib/bolt/transport/local/connection.rb +1 -1
- data/lib/bolt/transport/ssh.rb +1 -1
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
- data/lib/bolt/transport/winrm.rb +1 -1
- data/lib/bolt/transport/winrm/connection.rb +1 -1
- data/lib/bolt/util.rb +30 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/base_config.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/file_cache.rb +12 -12
- data/lib/bolt_server/transport_app.rb +125 -26
- data/lib/bolt_spec/bolt_context.rb +4 -4
- data/lib/bolt_spec/run.rb +3 -0
- metadata +11 -14
@@ -7,7 +7,7 @@ module Bolt
|
|
7
7
|
class YamlPlan
|
8
8
|
class Evaluator
|
9
9
|
def initialize(analytics = Bolt::Analytics::NoopClient.new)
|
10
|
-
@logger =
|
10
|
+
@logger = Bolt::Logger.logger(self)
|
11
11
|
@analytics = analytics
|
12
12
|
@evaluator = Puppet::Pops::Parser::EvaluatingParser.new
|
13
13
|
end
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -19,7 +19,7 @@ module Bolt
|
|
19
19
|
def initialize(config:, context:)
|
20
20
|
pdb_config = Bolt::PuppetDB::Config.load_config(config, context.boltdir)
|
21
21
|
@puppetdb_client = Bolt::PuppetDB::Client.new(pdb_config)
|
22
|
-
@logger =
|
22
|
+
@logger = Bolt::Logger.logger(self)
|
23
23
|
end
|
24
24
|
|
25
25
|
def name
|
data/lib/bolt/project.rb
CHANGED
@@ -17,37 +17,53 @@ module Bolt
|
|
17
17
|
}.freeze
|
18
18
|
|
19
19
|
attr_reader :path, :data, :config_file, :inventory_file, :modulepath, :hiera_config,
|
20
|
-
:puppetfile, :rerunfile, :type, :resource_types, :
|
20
|
+
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
21
21
|
:deprecations, :downloads, :plans_path
|
22
22
|
|
23
|
-
def self.default_project
|
24
|
-
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
|
23
|
+
def self.default_project(logs = [])
|
24
|
+
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
|
25
25
|
# If homedir isn't defined use the system config path
|
26
26
|
rescue ArgumentError
|
27
|
-
create_project(Bolt::Config.system_path, 'system')
|
27
|
+
create_project(Bolt::Config.system_path, 'system', logs)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Search recursively up the directory hierarchy for the Project. Look for a
|
31
31
|
# directory called Boltdir or a file called bolt.yaml (for a control repo
|
32
32
|
# type Project). Otherwise, repeat the check on each directory up the
|
33
33
|
# hierarchy, falling back to the default if we reach the root.
|
34
|
-
def self.find_boltdir(dir)
|
34
|
+
def self.find_boltdir(dir, logs = [])
|
35
35
|
dir = Pathname.new(dir)
|
36
36
|
|
37
37
|
if (dir + BOLTDIR_NAME).directory?
|
38
|
-
create_project(dir + BOLTDIR_NAME, 'embedded')
|
38
|
+
create_project(dir + BOLTDIR_NAME, 'embedded', logs)
|
39
39
|
elsif (dir + 'bolt.yaml').file? || (dir + 'bolt-project.yaml').file?
|
40
|
-
create_project(dir, 'local')
|
40
|
+
create_project(dir, 'local', logs)
|
41
41
|
elsif dir.root?
|
42
|
-
default_project
|
42
|
+
default_project(logs)
|
43
43
|
else
|
44
|
-
|
44
|
+
logs << { debug: "Did not detect Boltdir, bolt.yaml, or bolt-project.yaml at '#{dir}'. "\
|
45
|
+
"This directory won't be loaded as a project." }
|
46
|
+
find_boltdir(dir.parent, logs)
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
def self.create_project(path, type = 'option')
|
50
|
+
def self.create_project(path, type = 'option', logs = [])
|
49
51
|
fullpath = Pathname.new(path).expand_path
|
50
52
|
|
53
|
+
if type == 'user'
|
54
|
+
begin
|
55
|
+
# This is already expanded if the type is user
|
56
|
+
FileUtils.mkdir_p(path)
|
57
|
+
rescue StandardError
|
58
|
+
logs << { warn: "Could not create default project at #{path}. Continuing without a writeable project. "\
|
59
|
+
"Log and rerun files will not be written." }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if type == 'option' && !File.directory?(path)
|
64
|
+
raise Bolt::Error.new("Could not find project at #{path}", "bolt/project-error")
|
65
|
+
end
|
66
|
+
|
51
67
|
if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
|
52
68
|
raise Bolt::Error.new(
|
53
69
|
"Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
|
@@ -58,15 +74,18 @@ module Bolt
|
|
58
74
|
|
59
75
|
project_file = File.join(fullpath, 'bolt-project.yaml')
|
60
76
|
data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
|
61
|
-
|
77
|
+
default = type =~ /user|system/ ? 'default ' : ''
|
78
|
+
exist = File.exist?(File.expand_path(project_file))
|
79
|
+
logs << { info: "Loaded #{default}project from '#{fullpath}'" } if exist
|
80
|
+
new(data, path, type, logs)
|
62
81
|
end
|
63
82
|
|
64
|
-
def initialize(raw_data, path, type = 'option')
|
83
|
+
def initialize(raw_data, path, type = 'option', logs = [])
|
65
84
|
@path = Pathname.new(path).expand_path
|
66
85
|
|
67
86
|
@project_file = @path + 'bolt-project.yaml'
|
68
87
|
|
69
|
-
@
|
88
|
+
@logs = logs
|
70
89
|
@deprecations = []
|
71
90
|
if (@path + 'bolt.yaml').file? && project_file?
|
72
91
|
msg = "Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
|
@@ -88,7 +107,7 @@ module Bolt
|
|
88
107
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
89
108
|
if tc.any?
|
90
109
|
msg = "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}"
|
91
|
-
@
|
110
|
+
@logs << { warn: msg }
|
92
111
|
end
|
93
112
|
|
94
113
|
@data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }
|
@@ -98,7 +117,7 @@ module Bolt
|
|
98
117
|
@config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
|
99
118
|
if (@path + 'bolt.yaml').file?
|
100
119
|
msg = "bolt-project.yaml contains valid config keys, bolt.yaml will be ignored"
|
101
|
-
@
|
120
|
+
@logs << { warn: msg }
|
102
121
|
end
|
103
122
|
@project_file
|
104
123
|
else
|
@@ -114,7 +133,9 @@ module Bolt
|
|
114
133
|
# This API is used to prepend the project as a module to Puppet's internal
|
115
134
|
# module_references list. CHANGE AT YOUR OWN RISK
|
116
135
|
def to_h
|
117
|
-
{ path: @path.to_s,
|
136
|
+
{ path: @path.to_s,
|
137
|
+
name: name,
|
138
|
+
load_as_module?: load_as_module? }
|
118
139
|
end
|
119
140
|
|
120
141
|
def eql?(other)
|
@@ -126,6 +147,10 @@ module Bolt
|
|
126
147
|
@project_file.file?
|
127
148
|
end
|
128
149
|
|
150
|
+
def load_as_module?
|
151
|
+
!name.nil?
|
152
|
+
end
|
153
|
+
|
129
154
|
def name
|
130
155
|
@data['name']
|
131
156
|
end
|
@@ -138,6 +163,10 @@ module Bolt
|
|
138
163
|
@data['plans']
|
139
164
|
end
|
140
165
|
|
166
|
+
def modules
|
167
|
+
@data['modules']
|
168
|
+
end
|
169
|
+
|
141
170
|
def validate
|
142
171
|
if name
|
143
172
|
if name !~ Bolt::Module::MODULE_NAME_REGEX
|
@@ -151,7 +180,7 @@ module Bolt
|
|
151
180
|
end
|
152
181
|
else
|
153
182
|
message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
|
154
|
-
@
|
183
|
+
@logs << { warn: message }
|
155
184
|
end
|
156
185
|
|
157
186
|
%w[tasks plans].each do |conf|
|
@@ -159,6 +188,22 @@ module Bolt
|
|
159
188
|
raise Bolt::ValidationError, "'#{conf}' in bolt-project.yaml must be an array"
|
160
189
|
end
|
161
190
|
end
|
191
|
+
|
192
|
+
if @data['modules']
|
193
|
+
unless @data['modules'].is_a?(Array)
|
194
|
+
raise Bolt::ValidationError, "'modules' in bolt-project.yaml must be an array"
|
195
|
+
end
|
196
|
+
|
197
|
+
@data['modules'].each do |mod|
|
198
|
+
next if mod.is_a?(Hash)
|
199
|
+
raise Bolt::ValidationError, "Module declaration #{mod.inspect} must be a hash"
|
200
|
+
end
|
201
|
+
|
202
|
+
unknown_keys = data['modules'].flat_map(&:keys).uniq - %w[name version_requirement]
|
203
|
+
if unknown_keys.any?
|
204
|
+
@logs << { warn: "Ignoring unknown keys in module declarations: #{unknown_keys.join(', ')}." }
|
205
|
+
end
|
206
|
+
end
|
162
207
|
end
|
163
208
|
|
164
209
|
def check_deprecated_file
|
data/lib/bolt/puppetdb/client.rb
CHANGED
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -35,7 +35,7 @@ module Bolt
|
|
35
35
|
begin
|
36
36
|
config = JSON.parse(File.read(filepath)) if filepath
|
37
37
|
rescue StandardError => e
|
38
|
-
|
38
|
+
Bolt::Logger.logger(self).error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
|
39
39
|
end
|
40
40
|
|
41
41
|
config = config.fetch('puppetdb', {})
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/puppetfile/module'
|
5
|
+
|
6
|
+
# This class manages the logical contents of a Puppetfile. It includes methods
|
7
|
+
# for parsing a Puppetfile and its modules, resolving module dependencies,
|
8
|
+
# and writing a Puppetfile.
|
9
|
+
#
|
10
|
+
module Bolt
|
11
|
+
class Puppetfile
|
12
|
+
attr_reader :modules
|
13
|
+
|
14
|
+
def initialize(modules = [])
|
15
|
+
@modules = Set.new
|
16
|
+
add_modules(modules)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Loads a Puppetfile and parses its module specifications, returning a
|
20
|
+
# Bolt::Puppetfile object with the modules set.
|
21
|
+
#
|
22
|
+
def self.parse(path)
|
23
|
+
require 'puppetfile-resolver'
|
24
|
+
require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
|
25
|
+
|
26
|
+
begin
|
27
|
+
parsed = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(File.read(path))
|
28
|
+
rescue StandardError => e
|
29
|
+
raise Bolt::Error.new(
|
30
|
+
"Unable to parse Puppetfile #{path}: #{e.message}",
|
31
|
+
'bolt/puppetfile-parsing'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
unless parsed.valid?
|
36
|
+
raise Bolt::ValidationError,
|
37
|
+
"Unable to parse Puppetfile #{path}"
|
38
|
+
end
|
39
|
+
|
40
|
+
modules = parsed.modules.map do |mod|
|
41
|
+
Bolt::Puppetfile::Module.new(mod.owner, mod.name, mod.version)
|
42
|
+
end
|
43
|
+
|
44
|
+
new(modules)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Writes a Puppetfile that includes specifications for each of the
|
48
|
+
# modules.
|
49
|
+
#
|
50
|
+
def write(path, force: false)
|
51
|
+
if File.exist?(path) && !force
|
52
|
+
raise Bolt::FileError.new(
|
53
|
+
"Cannot overwrite existing Puppetfile at #{path}. To forcibly overwrite, "\
|
54
|
+
"run with the '--force' option.",
|
55
|
+
path
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
File.open(path, 'w') do |file|
|
60
|
+
file.puts '# This Puppetfile is managed by Bolt. Do not edit.'
|
61
|
+
modules.each { |mod| file.puts mod.to_spec }
|
62
|
+
file.puts
|
63
|
+
end
|
64
|
+
rescue SystemCallError => e
|
65
|
+
raise Bolt::FileError.new(
|
66
|
+
"#{e.message}: unable to write Puppetfile.",
|
67
|
+
path
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Resolves module dependencies using the puppetfile-resolver library. The
|
72
|
+
# resolver will return a document model including all module dependencies
|
73
|
+
# and the latest version that can be installed for each. The document model
|
74
|
+
# is parsed and turned into a Set of Bolt::Puppetfile::Module objects.
|
75
|
+
#
|
76
|
+
def resolve
|
77
|
+
require 'puppetfile-resolver'
|
78
|
+
|
79
|
+
# Build the document model from the modules.
|
80
|
+
model = PuppetfileResolver::Puppetfile::Document.new('')
|
81
|
+
|
82
|
+
@modules.each do |mod|
|
83
|
+
model.add_module(
|
84
|
+
PuppetfileResolver::Puppetfile::ForgeModule.new(mod.title).tap do |tap|
|
85
|
+
tap.version = mod.version || :latest
|
86
|
+
end
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Make sure the Puppetfile model is valid.
|
91
|
+
unless model.valid?
|
92
|
+
raise Bolt::ValidationError,
|
93
|
+
"Unable to resolve dependencies for modules: #{@modules.map(&:title).join(', ')}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create the resolver using the Puppetfile model. nil disables Puppet
|
97
|
+
# version restrictions.
|
98
|
+
resolver = PuppetfileResolver::Resolver.new(model, nil)
|
99
|
+
|
100
|
+
# Configure and resolve the dependency graph, catching any errors
|
101
|
+
# raised by puppetfile-resolver and re-raising them as Bolt errors.
|
102
|
+
begin
|
103
|
+
result = resolver.resolve(
|
104
|
+
cache: nil,
|
105
|
+
ui: nil,
|
106
|
+
module_paths: [],
|
107
|
+
allow_missing_modules: true
|
108
|
+
)
|
109
|
+
rescue StandardError => e
|
110
|
+
raise Bolt::Error.new(e.message, 'bolt/puppetfile-resolver-error')
|
111
|
+
end
|
112
|
+
|
113
|
+
# Validate that the modules exist.
|
114
|
+
missing_graph = result.specifications.select do |_name, spec|
|
115
|
+
spec.instance_of? PuppetfileResolver::Models::MissingModuleSpecification
|
116
|
+
end
|
117
|
+
|
118
|
+
if missing_graph.any?
|
119
|
+
titles = model.modules.each_with_object({}) do |mod, acc|
|
120
|
+
acc[mod.name] = mod.title
|
121
|
+
end
|
122
|
+
|
123
|
+
names = titles.values_at(*missing_graph.keys)
|
124
|
+
plural = names.count == 1 ? '' : 's'
|
125
|
+
|
126
|
+
raise Bolt::Error.new(
|
127
|
+
"Unknown module name#{plural} #{names.join(', ')}",
|
128
|
+
'bolt/unknown-modules'
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Filter the dependency graph to only include module specifications. This
|
133
|
+
# will only remove the Puppet version specification, which is not needed.
|
134
|
+
specs = result.specifications.select do |_name, spec|
|
135
|
+
spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
|
136
|
+
end
|
137
|
+
|
138
|
+
@modules = specs.map do |_name, spec|
|
139
|
+
Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
|
140
|
+
end.to_set
|
141
|
+
end
|
142
|
+
|
143
|
+
# Adds to the set of modules.
|
144
|
+
#
|
145
|
+
def add_modules(modules)
|
146
|
+
modules.each do |mod|
|
147
|
+
case mod
|
148
|
+
when Bolt::Puppetfile::Module
|
149
|
+
@modules << mod
|
150
|
+
when Hash
|
151
|
+
@modules << Bolt::Puppetfile::Module.from_hash(mod)
|
152
|
+
else
|
153
|
+
raise Bolt::ValidationError, "Module must be a Bolt::Puppetfile::Module or Hash."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@modules
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'r10k/cli'
|
4
|
+
require 'bolt/r10k_log_proxy'
|
5
|
+
require 'bolt/error'
|
6
|
+
|
7
|
+
# This class is used to install modules from a Puppetfile to a module directory.
|
8
|
+
#
|
9
|
+
module Bolt
|
10
|
+
class Puppetfile
|
11
|
+
class Installer
|
12
|
+
def initialize(config = {})
|
13
|
+
@config = config
|
14
|
+
end
|
15
|
+
|
16
|
+
def install(path, moduledir)
|
17
|
+
unless File.exist?(path)
|
18
|
+
raise Bolt::FileError.new(
|
19
|
+
"Could not find a Puppetfile at #{path}",
|
20
|
+
path
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
r10k_opts = {
|
25
|
+
root: File.dirname(path),
|
26
|
+
puppetfile: path.to_s,
|
27
|
+
moduledir: moduledir.to_s
|
28
|
+
}
|
29
|
+
|
30
|
+
settings = R10K::Settings.global_settings.evaluate(@config)
|
31
|
+
R10K::Initializers::GlobalInitializer.new(settings).call
|
32
|
+
install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil)
|
33
|
+
|
34
|
+
# Override the r10k logger with a proxy to our own logger
|
35
|
+
R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
|
36
|
+
|
37
|
+
install_action.call
|
38
|
+
rescue R10K::Error => e
|
39
|
+
raise PuppetfileError, e
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
# This class represents a module specification. It used by the Bolt::Puppetfile
|
6
|
+
# class to have a consistent API for accessing a module's attributes.
|
7
|
+
#
|
8
|
+
module Bolt
|
9
|
+
class Puppetfile
|
10
|
+
class Module
|
11
|
+
attr_reader :owner, :name, :version
|
12
|
+
|
13
|
+
def initialize(owner, name, version = nil)
|
14
|
+
@owner = owner
|
15
|
+
@name = name
|
16
|
+
@version = version
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates a new module from a hash.
|
20
|
+
#
|
21
|
+
def self.from_hash(mod)
|
22
|
+
unless mod['name'].is_a?(String)
|
23
|
+
raise Bolt::ValidationError,
|
24
|
+
"Module name must be a String, not #{mod['name'].inspect}"
|
25
|
+
end
|
26
|
+
|
27
|
+
owner, name = mod['name'].tr('/', '-').split('-', 2)
|
28
|
+
|
29
|
+
unless owner && name
|
30
|
+
raise Bolt::ValidationError, "Module name #{mod['name']} must include both the owner and module name."
|
31
|
+
end
|
32
|
+
|
33
|
+
new(owner, name, mod['version_requirement'])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the module's title.
|
37
|
+
#
|
38
|
+
def title
|
39
|
+
"#{@owner}-#{@name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks two modules for equality.
|
43
|
+
#
|
44
|
+
def eql?(other)
|
45
|
+
self.class == other.class &&
|
46
|
+
@owner == other.owner &&
|
47
|
+
@name == other.name &&
|
48
|
+
versions_intersect?(other)
|
49
|
+
end
|
50
|
+
alias == eql?
|
51
|
+
|
52
|
+
# Returns true if the versions of two modules intersect. Used to determine
|
53
|
+
# if an installed module satisfies the version requirement of another.
|
54
|
+
#
|
55
|
+
def versions_intersect?(other)
|
56
|
+
range = ::SemanticPuppet::VersionRange.parse(@version || '')
|
57
|
+
other_range = ::SemanticPuppet::VersionRange.parse(other.version || '')
|
58
|
+
|
59
|
+
range.intersection(other_range) != ::SemanticPuppet::VersionRange::EMPTY_RANGE
|
60
|
+
end
|
61
|
+
|
62
|
+
# Hashes the module.
|
63
|
+
#
|
64
|
+
def hash
|
65
|
+
[@owner, @name].hash
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns a hash representation similar to the module
|
69
|
+
# declaration.
|
70
|
+
#
|
71
|
+
def to_hash
|
72
|
+
{
|
73
|
+
'name' => title,
|
74
|
+
'version_requirement' => version
|
75
|
+
}.compact
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the Puppetfile specification for the module.
|
79
|
+
#
|
80
|
+
def to_spec
|
81
|
+
if @version
|
82
|
+
"mod #{title.inspect}, #{@version.inspect}"
|
83
|
+
else
|
84
|
+
"mod #{title.inspect}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|