bolt 2.27.0 → 2.32.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 +13 -12
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/applicator.rb +14 -14
- data/lib/bolt/bolt_option_parser.rb +74 -22
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +178 -127
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +38 -9
- data/lib/bolt/config/transport/options.rb +1 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/logger.rb +26 -19
- data/lib/bolt/module_installer.rb +197 -0
- data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
- data/lib/bolt/module_installer/puppetfile.rb +117 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/resolver.rb +76 -0
- data/lib/bolt/module_installer/specs.rb +93 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +84 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
- data/lib/bolt/outputter.rb +2 -45
- data/lib/bolt/outputter/human.rb +78 -18
- data/lib/bolt/outputter/json.rb +22 -7
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +29 -25
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +32 -22
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +200 -0
- data/lib/bolt/shell/bash.rb +4 -3
- data/lib/bolt/transport/base.rb +4 -4
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +51 -10
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_server/file_cache.rb +11 -11
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +93 -13
- data/lib/bolt_spec/bolt_context.rb +8 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +30 -11
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt/puppetfile.rb +0 -160
- data/lib/bolt/puppetfile/module.rb +0 -66
- data/lib/bolt_server/pe/pal.rb +0 -67
@@ -0,0 +1,200 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/project_migrator/base'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class ProjectMigrator
|
7
|
+
class Modules < Base
|
8
|
+
def migrate(project, configured_modulepath)
|
9
|
+
return true unless project.modules.nil?
|
10
|
+
|
11
|
+
@outputter.print_message "Migrating project modules\n\n"
|
12
|
+
|
13
|
+
config = project.project_file
|
14
|
+
puppetfile = project.puppetfile
|
15
|
+
managed_moduledir = project.managed_moduledir
|
16
|
+
modulepath = [(project.path + 'modules').to_s,
|
17
|
+
(project.path + 'site-modules').to_s,
|
18
|
+
(project.path + 'site').to_s]
|
19
|
+
|
20
|
+
# Notify user to manually migrate modules if using non-default modulepath
|
21
|
+
if configured_modulepath != modulepath
|
22
|
+
@outputter.print_action_step(
|
23
|
+
"Project has a non-default configured modulepath, unable to automatically "\
|
24
|
+
"migrate project modules. To migrate project modules manually, see "\
|
25
|
+
"http://pup.pt/bolt-modules"
|
26
|
+
)
|
27
|
+
true
|
28
|
+
# Migrate modules from Puppetfile
|
29
|
+
elsif File.exist?(puppetfile)
|
30
|
+
migrate_modules_from_puppetfile(config, puppetfile, managed_moduledir, modulepath)
|
31
|
+
# Migrate modules to updated modulepath
|
32
|
+
else
|
33
|
+
consolidate_modules(modulepath)
|
34
|
+
update_project_config([], config)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Migrates modules by reading a Puppetfile and prompting the user for
|
39
|
+
# which ones are direct dependencies for the project. Once the user has
|
40
|
+
# selected the direct dependencies, this will resolve the modules, write a
|
41
|
+
# new Puppetfile, install the modules, and then move any remaining modules
|
42
|
+
# to the new moduledir.
|
43
|
+
#
|
44
|
+
private def migrate_modules_from_puppetfile(config, puppetfile_path, managed_moduledir, modulepath)
|
45
|
+
require 'bolt/module_installer/installer'
|
46
|
+
require 'bolt/module_installer/puppetfile'
|
47
|
+
require 'bolt/module_installer/resolver'
|
48
|
+
require 'bolt/module_installer/specs'
|
49
|
+
|
50
|
+
begin
|
51
|
+
@outputter.print_action_step("Parsing Puppetfile at #{puppetfile_path}")
|
52
|
+
puppetfile = Bolt::ModuleInstaller::Puppetfile.parse(puppetfile_path, skip_unsupported_modules: true)
|
53
|
+
rescue Bolt::Error => e
|
54
|
+
@outputter.print_action_error("#{e.message}\nSkipping module migration.")
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Prompt for direct dependencies
|
59
|
+
modules = select_modules(puppetfile.modules)
|
60
|
+
|
61
|
+
# Create specs to resolve from
|
62
|
+
specs = Bolt::ModuleInstaller::Specs.new(modules.map(&:to_hash))
|
63
|
+
|
64
|
+
# Attempt to resolve dependencies
|
65
|
+
begin
|
66
|
+
@outputter.print_message('')
|
67
|
+
@outputter.print_action_step("Resolving module dependencies, this may take a moment")
|
68
|
+
puppetfile = Bolt::ModuleInstaller::Resolver.new.resolve(specs)
|
69
|
+
rescue Bolt::Error => e
|
70
|
+
@outputter.print_action_error("#{e.message}\nSkipping module migration.")
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
|
74
|
+
migrate_managed_modules(puppetfile, puppetfile_path, managed_moduledir)
|
75
|
+
|
76
|
+
# Move remaining modules to 'modules'
|
77
|
+
consolidate_modules(modulepath)
|
78
|
+
|
79
|
+
# Delete old modules that are now managed
|
80
|
+
delete_modules(modulepath.first, puppetfile.modules)
|
81
|
+
|
82
|
+
# Add modules to project
|
83
|
+
update_project_config(modules.map(&:to_hash), config)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Migrates the managed modules. If modules were selected to be managed,
|
87
|
+
# the Puppetfile is rewritten and modules are installed. If no modules
|
88
|
+
# were selected, the Puppetfile is deleted.
|
89
|
+
#
|
90
|
+
private def migrate_managed_modules(puppetfile, puppetfile_path, managed_moduledir)
|
91
|
+
if puppetfile.modules.any?
|
92
|
+
# Show the new Puppetfile content
|
93
|
+
message = "Generated new Puppetfile content:\n\n"
|
94
|
+
message += puppetfile.modules.map(&:to_spec).join("\n").to_s
|
95
|
+
@outputter.print_action_step(message)
|
96
|
+
|
97
|
+
# Write Puppetfile
|
98
|
+
@outputter.print_action_step("Updating Puppetfile at #{puppetfile_path}")
|
99
|
+
puppetfile.write(puppetfile_path, managed_moduledir)
|
100
|
+
|
101
|
+
# Install Puppetfile
|
102
|
+
@outputter.print_action_step("Syncing modules from #{puppetfile_path} to #{managed_moduledir}")
|
103
|
+
Bolt::ModuleInstaller::Installer.new.install(puppetfile_path, managed_moduledir)
|
104
|
+
else
|
105
|
+
@outputter.print_action_step(
|
106
|
+
"Project does not include any managed modules, deleting Puppetfile "\
|
107
|
+
"at #{puppetfile_path}"
|
108
|
+
)
|
109
|
+
FileUtils.rm(puppetfile_path)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Prompts the user to select modules, returning a list of
|
114
|
+
# the selected modules.
|
115
|
+
#
|
116
|
+
private def select_modules(modules)
|
117
|
+
@outputter.print_action_step(
|
118
|
+
"Select modules that are direct dependencies of your project. Bolt will "\
|
119
|
+
"automatically manage dependencies for each module selected, so do not "\
|
120
|
+
"select a module's dependencies unless you use content from it directly "\
|
121
|
+
"in your project."
|
122
|
+
)
|
123
|
+
|
124
|
+
all = Bolt::Util.prompt_yes_no("Select all modules?", @outputter)
|
125
|
+
return modules if all
|
126
|
+
|
127
|
+
modules.select do |mod|
|
128
|
+
Bolt::Util.prompt_yes_no("Select #{mod.full_name}?", @outputter)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Consolidates all modules on the modulepath to 'modules'.
|
133
|
+
#
|
134
|
+
private def consolidate_modules(modulepath)
|
135
|
+
moduledir, *sources = modulepath
|
136
|
+
|
137
|
+
sources.select! { |source| Dir.exist?(source) }
|
138
|
+
|
139
|
+
if sources.any?
|
140
|
+
@outputter.print_action_step(
|
141
|
+
"Moving modules from #{sources.join(', ')} to #{moduledir}"
|
142
|
+
)
|
143
|
+
|
144
|
+
FileUtils.mkdir_p(moduledir)
|
145
|
+
move_modules(moduledir, sources)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Moves modules from a list of source directories to the specified
|
150
|
+
# moduledir, deleting the source directory after it's done.
|
151
|
+
#
|
152
|
+
private def move_modules(moduledir, sources)
|
153
|
+
moduledir = Pathname.new(moduledir)
|
154
|
+
|
155
|
+
sources.each do |source|
|
156
|
+
source = Pathname.new(source)
|
157
|
+
|
158
|
+
source.each_child do |mod|
|
159
|
+
next unless mod.directory?
|
160
|
+
next if (moduledir + mod.basename).directory?
|
161
|
+
FileUtils.mv(mod, moduledir)
|
162
|
+
end
|
163
|
+
|
164
|
+
FileUtils.rm_r(source)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Deletes modules from a specified directory.
|
169
|
+
#
|
170
|
+
private def delete_modules(moduledir, modules)
|
171
|
+
@outputter.print_action_step("Cleaning up #{moduledir}")
|
172
|
+
moduledir = Pathname.new(moduledir)
|
173
|
+
|
174
|
+
modules.each do |mod|
|
175
|
+
path = moduledir + mod.name
|
176
|
+
FileUtils.rm_r(path) if path.directory?
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Adds a list of modules to the project configuration file.
|
181
|
+
#
|
182
|
+
private def update_project_config(modules, config_file)
|
183
|
+
@outputter.print_action_step("Updating project configuration at #{config_file}")
|
184
|
+
data = Bolt::Util.read_optional_yaml_hash(config_file, 'project')
|
185
|
+
data.merge!('modules' => modules)
|
186
|
+
data.delete('modulepath')
|
187
|
+
|
188
|
+
begin
|
189
|
+
File.write(config_file, data.to_yaml)
|
190
|
+
true
|
191
|
+
rescue StandardError => e
|
192
|
+
raise Bolt::FileError.new(
|
193
|
+
"Unable to write to #{config_file}: #{e.message}",
|
194
|
+
config_file
|
195
|
+
)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -143,7 +143,7 @@ module Bolt
|
|
143
143
|
|
144
144
|
execute_options[:stdin] = stdin
|
145
145
|
execute_options[:sudoable] = true if run_as
|
146
|
-
output = execute(remote_task_path, execute_options)
|
146
|
+
output = execute(remote_task_path, **execute_options)
|
147
147
|
end
|
148
148
|
Bolt::Result.for_task(target, output.stdout.string,
|
149
149
|
output.stderr.string,
|
@@ -202,13 +202,14 @@ module Bolt
|
|
202
202
|
end
|
203
203
|
|
204
204
|
def handle_sudo_errors(err)
|
205
|
-
|
205
|
+
case err
|
206
|
+
when /^#{conn.user} is not in the sudoers file\./
|
206
207
|
@logger.trace { err }
|
207
208
|
raise Bolt::Node::EscalateError.new(
|
208
209
|
"User #{conn.user} does not have sudo permission on #{target}",
|
209
210
|
'SUDO_DENIED'
|
210
211
|
)
|
211
|
-
|
212
|
+
when /^Sorry, try again\./
|
212
213
|
@logger.trace { err }
|
213
214
|
raise Bolt::Node::EscalateError.new(
|
214
215
|
"Sudo password for user #{conn.user} not recognized on #{target}",
|
data/lib/bolt/transport/base.rb
CHANGED
@@ -47,10 +47,10 @@ module Bolt
|
|
47
47
|
callback&.call(type: :node_start, target: target)
|
48
48
|
|
49
49
|
result = begin
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
yield
|
51
|
+
rescue StandardError, NotImplementedError => e
|
52
|
+
Bolt::Result.from_exception(target, e, action: action)
|
53
|
+
end
|
54
54
|
|
55
55
|
callback&.call(type: :node_result, result: result)
|
56
56
|
result
|
@@ -30,7 +30,7 @@ module Bolt
|
|
30
30
|
@transport_logger = transport_logger
|
31
31
|
@logger.trace("Initializing ssh connection to #{@target.safe_name}")
|
32
32
|
|
33
|
-
if target.options['private-key']
|
33
|
+
if target.options['private-key'].instance_of?(String)
|
34
34
|
begin
|
35
35
|
Bolt::Util.validate_file('ssh key', target.options['private-key'])
|
36
36
|
rescue Bolt::FileError => e
|
data/lib/bolt/util.rb
CHANGED
@@ -3,6 +3,25 @@
|
|
3
3
|
module Bolt
|
4
4
|
module Util
|
5
5
|
class << self
|
6
|
+
# Gets input for an argument.
|
7
|
+
def get_arg_input(value)
|
8
|
+
if value.start_with?('@')
|
9
|
+
file = value.sub(/^@/, '')
|
10
|
+
read_arg_file(file)
|
11
|
+
elsif value == '-'
|
12
|
+
$stdin.read
|
13
|
+
else
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Reads a file passed as an argument to a command.
|
19
|
+
def read_arg_file(file)
|
20
|
+
File.read(File.expand_path(file))
|
21
|
+
rescue StandardError => e
|
22
|
+
raise Bolt::FileError.new("Error attempting to read #{file}: #{e}", file)
|
23
|
+
end
|
24
|
+
|
6
25
|
def read_yaml_hash(path, file_name)
|
7
26
|
require 'yaml'
|
8
27
|
|
@@ -179,16 +198,16 @@ module Bolt
|
|
179
198
|
# object was frozen
|
180
199
|
frozen = obj.frozen?
|
181
200
|
cl = begin
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
201
|
+
obj.clone(freeze: false)
|
202
|
+
# Some datatypes, such as FalseClass, can't be unfrozen. These
|
203
|
+
# aren't the types we recurse on, so we can leave them frozen
|
204
|
+
rescue ArgumentError => e
|
205
|
+
if e.message =~ /can't unfreeze/
|
206
|
+
obj.clone
|
207
|
+
else
|
208
|
+
raise e
|
209
|
+
end
|
210
|
+
end
|
192
211
|
rescue *error_types
|
193
212
|
cloned[obj.object_id] = obj
|
194
213
|
obj
|
@@ -280,6 +299,28 @@ module Bolt
|
|
280
299
|
raise Bolt::ValidationError, "path must be a String, received #{path.class} #{path}" unless path.is_a?(String)
|
281
300
|
path.split(%r{[/\\]}).last
|
282
301
|
end
|
302
|
+
|
303
|
+
# Prompts yes or no, returning true for yes and false for no.
|
304
|
+
#
|
305
|
+
def prompt_yes_no(prompt, outputter)
|
306
|
+
choices = {
|
307
|
+
'y' => true,
|
308
|
+
'yes' => true,
|
309
|
+
'n' => false,
|
310
|
+
'no' => false
|
311
|
+
}
|
312
|
+
|
313
|
+
loop do
|
314
|
+
outputter.print_prompt("#{prompt} ([y]es/[n]o) ")
|
315
|
+
response = $stdin.gets.to_s.downcase.chomp
|
316
|
+
|
317
|
+
if choices.key?(response)
|
318
|
+
return choices[response]
|
319
|
+
else
|
320
|
+
outputter.print_prompt_error("Invalid response, must pick [y]es or [n]o")
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
283
324
|
end
|
284
325
|
end
|
285
326
|
end
|
data/lib/bolt/version.rb
CHANGED
data/lib/bolt_server/acl.rb
CHANGED
@@ -7,7 +7,7 @@ module BoltServer
|
|
7
7
|
class BaseConfig
|
8
8
|
def config_keys
|
9
9
|
%w[host port ssl-cert ssl-key ssl-ca-cert
|
10
|
-
ssl-cipher-suites loglevel logfile
|
10
|
+
ssl-cipher-suites loglevel logfile allowlist projects-dir]
|
11
11
|
end
|
12
12
|
|
13
13
|
def env_keys
|
@@ -98,8 +98,8 @@ module BoltServer
|
|
98
98
|
raise Bolt::ValidationError, "Configured 'ssl-cipher-suites' must be an array of cipher suite names"
|
99
99
|
end
|
100
100
|
|
101
|
-
unless @data['
|
102
|
-
raise Bolt::ValidationError, "Configured '
|
101
|
+
unless @data['allowlist'].nil? || @data['allowlist'].is_a?(Array)
|
102
|
+
raise Bolt::ValidationError, "Configured 'allowlist' must be an array of names"
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -64,17 +64,17 @@ module BoltServer
|
|
64
64
|
|
65
65
|
def client
|
66
66
|
@client ||= begin
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
67
|
+
uri = URI(@config['file-server-uri'])
|
68
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
69
|
+
https.use_ssl = true
|
70
|
+
https.ssl_version = :TLSv1_2
|
71
|
+
https.ca_file = @config['ssl-ca-cert']
|
72
|
+
https.cert = OpenSSL::X509::Certificate.new(ssl_cert)
|
73
|
+
https.key = OpenSSL::PKey::RSA.new(ssl_key)
|
74
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
75
|
+
https.open_timeout = @config['file-server-conn-timeout']
|
76
|
+
https
|
77
|
+
end
|
78
78
|
end
|
79
79
|
|
80
80
|
def request_file(path, params, file)
|
@@ -63,9 +63,24 @@
|
|
63
63
|
"environment": {
|
64
64
|
"description": "Environment the task is in",
|
65
65
|
"type": "string"
|
66
|
+
},
|
67
|
+
"project": {
|
68
|
+
"description": "Project the task is in",
|
69
|
+
"type": "string"
|
66
70
|
}
|
67
71
|
},
|
68
|
-
"
|
72
|
+
"oneOf": [
|
73
|
+
{
|
74
|
+
"required": [
|
75
|
+
"environment"
|
76
|
+
]
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"required": [
|
80
|
+
"project"
|
81
|
+
]
|
82
|
+
}
|
83
|
+
],
|
69
84
|
"additionalProperties": true
|
70
85
|
}
|
71
86
|
},
|
@@ -90,5 +105,5 @@
|
|
90
105
|
}
|
91
106
|
},
|
92
107
|
"required": ["name", "files"],
|
93
|
-
"additionalProperties":
|
108
|
+
"additionalProperties": true
|
94
109
|
}
|
@@ -14,7 +14,9 @@ require 'json-schema'
|
|
14
14
|
|
15
15
|
# These are only needed for the `/plans` endpoint.
|
16
16
|
require 'puppet'
|
17
|
-
|
17
|
+
|
18
|
+
# Needed by the `/project_file_metadatas` endpoint
|
19
|
+
require 'puppet/file_serving/fileset'
|
18
20
|
|
19
21
|
module BoltServer
|
20
22
|
class TransportApp < Sinatra::Base
|
@@ -35,6 +37,17 @@ module BoltServer
|
|
35
37
|
transport-winrm
|
36
38
|
].freeze
|
37
39
|
|
40
|
+
# PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
|
41
|
+
# in Bolt::PAL. Paths and variable names are similar to what exists in
|
42
|
+
# Bolt::PAL, but with a 'PE' prefix.
|
43
|
+
PE_BOLTLIB_PATH = '/opt/puppetlabs/server/apps/bolt-server/pe-bolt-modules'
|
44
|
+
|
45
|
+
# For now at least, we maintain an entirely separate codedir from
|
46
|
+
# puppetserver by default, so that filesync can work properly. If filesync
|
47
|
+
# is not used, this can instead match the usual puppetserver codedir.
|
48
|
+
# See the `orchestrator.bolt.codedir` tk config setting.
|
49
|
+
DEFAULT_BOLT_CODEDIR = '/opt/puppetlabs/server/data/orchestration-services/code'
|
50
|
+
|
38
51
|
def initialize(config)
|
39
52
|
@config = config
|
40
53
|
@schemas = Hash[REQUEST_SCHEMAS.map do |basename|
|
@@ -191,10 +204,52 @@ module BoltServer
|
|
191
204
|
[@executor.run_script(target, file_location, body['arguments'])]
|
192
205
|
end
|
193
206
|
|
207
|
+
# This function is nearly identical to Bolt::Pal's `with_puppet_settings` with the
|
208
|
+
# one difference that we set the codedir to point to actual code, rather than the
|
209
|
+
# tmpdir. We only use this funtion inside the Modulepath initializer so that Puppet
|
210
|
+
# is correctly configured to pull environment configuration correctly. If we don't
|
211
|
+
# set codedir in this way: when we try to load and interpolate the modulepath it
|
212
|
+
# won't correctly load.
|
213
|
+
#
|
214
|
+
# WARNING: THIS FUNCTION SHOULD ONLY BE CALLED INSIDE A SYNCHRONIZED PAL MUTEX
|
215
|
+
def with_pe_pal_init_settings(codedir, environmentpath, basemodulepath)
|
216
|
+
Dir.mktmpdir('pe-bolt') do |dir|
|
217
|
+
cli = []
|
218
|
+
Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
|
219
|
+
dir = setting == :codedir ? codedir : dir
|
220
|
+
cli << "--#{setting}" << dir
|
221
|
+
end
|
222
|
+
cli << "--environmentpath" << environmentpath
|
223
|
+
cli << "--basemodulepath" << basemodulepath
|
224
|
+
Puppet.settings.send(:clear_everything_for_tests)
|
225
|
+
Puppet.initialize_settings(cli)
|
226
|
+
yield
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Use puppet to identify the modulepath from an environment.
|
231
|
+
#
|
232
|
+
# WARNING: THIS FUNCTION SHOULD ONLY BE CALLED INSIDE A SYNCHRONIZED PAL MUTEX
|
233
|
+
def modulepath_from_environment(environment_name)
|
234
|
+
codedir = DEFAULT_BOLT_CODEDIR
|
235
|
+
environmentpath = "#{codedir}/environments"
|
236
|
+
basemodulepath = "#{codedir}/modules:/opt/puppetlabs/puppet/modules"
|
237
|
+
modulepath_dirs = nil
|
238
|
+
with_pe_pal_init_settings(codedir, environmentpath, basemodulepath) do
|
239
|
+
environment = Puppet.lookup(:environments).get!(environment_name)
|
240
|
+
modulepath_dirs = environment.modulepath
|
241
|
+
end
|
242
|
+
modulepath_dirs
|
243
|
+
end
|
244
|
+
|
194
245
|
def in_pe_pal_env(environment)
|
195
246
|
return [400, '`environment` is a required argument'] if environment.nil?
|
196
247
|
@pal_mutex.synchronize do
|
197
|
-
|
248
|
+
modulepath_obj = Bolt::Config::Modulepath.new(
|
249
|
+
modulepath_from_environment(environment),
|
250
|
+
boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH]
|
251
|
+
)
|
252
|
+
pal = Bolt::PAL.new(modulepath_obj, nil, nil)
|
198
253
|
yield pal
|
199
254
|
rescue Puppet::Environments::EnvironmentNotFound
|
200
255
|
[400, {
|
@@ -212,17 +267,12 @@ module BoltServer
|
|
212
267
|
return [400, "`project_ref`: #{project_dir} does not exist"] unless Dir.exist?(project_dir)
|
213
268
|
@pal_mutex.synchronize do
|
214
269
|
project = Bolt::Project.create_project(project_dir)
|
215
|
-
bolt_config = Bolt::Config.from_project(project, {})
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
Bolt::PAL::MODULES_PATH
|
222
|
-
]
|
223
|
-
# CODEREVIEW: I *think* this is the only thing we need to make different between bolt's PAL. Is it acceptable
|
224
|
-
# to hack this? Modulepath is currently a readable attribute, could we make it writeable?
|
225
|
-
pal.instance_variable_set(:@modulepath, module_path)
|
270
|
+
bolt_config = Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
|
271
|
+
modulepath_object = Bolt::Config::Modulepath.new(
|
272
|
+
bolt_config.modulepath,
|
273
|
+
boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH]
|
274
|
+
)
|
275
|
+
pal = Bolt::PAL.new(modulepath_object, nil, nil, nil, nil, nil, bolt_config.project)
|
226
276
|
context = {
|
227
277
|
pal: pal,
|
228
278
|
config: bolt_config
|
@@ -307,6 +357,23 @@ module BoltServer
|
|
307
357
|
plans.map { |plan_name| { 'name' => plan_name } }
|
308
358
|
end
|
309
359
|
|
360
|
+
def file_metadatas(pal, module_name, file)
|
361
|
+
pal.in_bolt_compiler do
|
362
|
+
mod = Puppet.lookup(:current_environment).module(module_name)
|
363
|
+
raise ArgumentError, "`module_name`: #{module_name} does not exist" unless mod
|
364
|
+
abs_file_path = mod.file(file)
|
365
|
+
raise ArgumentError, "`file`: #{file} does not exist inside the module's 'files' directory" unless abs_file_path
|
366
|
+
fileset = Puppet::FileServing::Fileset.new(abs_file_path, 'recurse' => 'yes')
|
367
|
+
Puppet::FileServing::Fileset.merge(fileset).collect do |relative_file_path, base_path|
|
368
|
+
metadata = Puppet::FileServing::Metadata.new(base_path, relative_path: relative_file_path)
|
369
|
+
metadata.checksum_type = 'sha256'
|
370
|
+
metadata.links = 'follow'
|
371
|
+
metadata.collect
|
372
|
+
metadata.to_data_hash
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
310
377
|
get '/' do
|
311
378
|
200
|
312
379
|
end
|
@@ -550,6 +617,19 @@ module BoltServer
|
|
550
617
|
end
|
551
618
|
end
|
552
619
|
|
620
|
+
# Implements puppetserver's file_metadatas endpoint for projects.
|
621
|
+
#
|
622
|
+
# @param project_ref [String] the project_ref to fetch the file metadatas from
|
623
|
+
get '/project_file_metadatas/:module_name/*' do
|
624
|
+
in_bolt_project(params['project_ref']) do |context|
|
625
|
+
file = params[:splat].first
|
626
|
+
metadatas = file_metadatas(context[:pal], params[:module_name], file)
|
627
|
+
[200, metadatas.to_json]
|
628
|
+
end
|
629
|
+
rescue ArgumentError => e
|
630
|
+
[400, e.message]
|
631
|
+
end
|
632
|
+
|
553
633
|
error 404 do
|
554
634
|
err = Bolt::Error.new("Could not find route #{request.path}",
|
555
635
|
'boltserver/not-found')
|