bolt 3.0.1 → 3.6.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 -11
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- data/bolt-modules/boltlib/types/planresult.pp +1 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/lib/bolt/analytics.rb +4 -8
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +6 -3
- data/lib/bolt/cli.rb +123 -37
- data/lib/bolt/config.rb +15 -7
- data/lib/bolt/config/options.rb +62 -12
- data/lib/bolt/config/transport/lxd.rb +23 -0
- data/lib/bolt/config/transport/options.rb +8 -1
- data/lib/bolt/config/transport/podman.rb +33 -0
- data/lib/bolt/container_result.rb +105 -0
- data/lib/bolt/error.rb +15 -0
- data/lib/bolt/executor.rb +37 -18
- data/lib/bolt/inventory/options.rb +9 -0
- data/lib/bolt/inventory/target.rb +16 -0
- data/lib/bolt/logger.rb +8 -0
- data/lib/bolt/module_installer.rb +2 -2
- data/lib/bolt/module_installer/puppetfile.rb +2 -2
- data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
- data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
- data/lib/bolt/node/output.rb +14 -4
- data/lib/bolt/outputter/human.rb +259 -90
- data/lib/bolt/outputter/json.rb +3 -1
- data/lib/bolt/outputter/logger.rb +17 -0
- data/lib/bolt/pal.rb +25 -4
- data/lib/bolt/pal/yaml_plan.rb +1 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
- data/lib/bolt/pal/yaml_plan/step.rb +91 -31
- data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
- data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
- data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
- data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
- data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
- data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
- data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
- data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
- data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
- data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
- data/lib/bolt/plan_creator.rb +1 -1
- data/lib/bolt/plugin.rb +13 -11
- data/lib/bolt/project_manager.rb +1 -1
- data/lib/bolt/project_manager/module_migrator.rb +1 -1
- data/lib/bolt/result.rb +11 -15
- data/lib/bolt/shell.rb +16 -0
- data/lib/bolt/shell/bash.rb +61 -31
- data/lib/bolt/shell/bash/tmpdir.rb +2 -2
- data/lib/bolt/shell/powershell.rb +34 -12
- data/lib/bolt/shell/powershell/snippets.rb +30 -3
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/base.rb +0 -9
- data/lib/bolt/transport/docker.rb +2 -126
- data/lib/bolt/transport/docker/connection.rb +81 -167
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/lxd/connection.rb +99 -0
- data/lib/bolt/transport/orch.rb +13 -5
- data/lib/bolt/transport/podman.rb +19 -0
- data/lib/bolt/transport/podman/connection.rb +98 -0
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/transport/winrm/connection.rb +1 -1
- data/lib/bolt/util.rb +42 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +64 -33
- data/lib/bolt_spec/bolt_context.rb +9 -4
- data/lib/bolt_spec/plans.rb +1 -109
- data/lib/bolt_spec/plans/action_stubs.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
- data/lib/bolt_spec/plans/mock_executor.rb +91 -7
- data/modules/puppet_connect/plans/test_input_data.pp +65 -7
- metadata +12 -2
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/logger'
|
4
|
+
require 'bolt/node/errors'
|
5
|
+
require 'bolt/transport/simple'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
module Transport
|
9
|
+
class LXD < Simple
|
10
|
+
def provided_features
|
11
|
+
['shell']
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_connection(target, options = {})
|
15
|
+
Bolt::Logger.warn_once("lxd_experimental",
|
16
|
+
"The LXD transport is experimental, and might "\
|
17
|
+
"include breaking changes between minor versions.")
|
18
|
+
conn = Connection.new(target, options)
|
19
|
+
conn.connect
|
20
|
+
yield conn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'bolt/transport/lxd/connection'
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logging'
|
4
|
+
require 'bolt/node/errors'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
module Transport
|
8
|
+
class LXD < Simple
|
9
|
+
class Connection
|
10
|
+
attr_reader :user, :target
|
11
|
+
|
12
|
+
def initialize(target, options)
|
13
|
+
raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
|
14
|
+
|
15
|
+
@target = target
|
16
|
+
@user = ENV['USER'] || Etc.getlogin
|
17
|
+
@options = options
|
18
|
+
@logger = Bolt::Logger.logger(target.safe_name)
|
19
|
+
@logger.trace("Initializing LXD connection to #{target.safe_name}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def shell
|
23
|
+
Bolt::Shell::Bash.new(target, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def container_id
|
27
|
+
"#{@target.transport_config['remote']}:#{@target.host}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def connect
|
31
|
+
out, err, status = execute_local_command(%W[list #{container_id} --format json])
|
32
|
+
unless status.exitstatus.zero?
|
33
|
+
raise "Error listing available containers: #{err}"
|
34
|
+
end
|
35
|
+
containers = JSON.parse(out)
|
36
|
+
if containers.empty?
|
37
|
+
raise "Could not find a container with name or ID matching '#{container_id}'"
|
38
|
+
end
|
39
|
+
@logger.trace("Opened session")
|
40
|
+
true
|
41
|
+
rescue StandardError => e
|
42
|
+
raise Bolt::Node::ConnectError.new(
|
43
|
+
"Failed to connect to #{container_id}: #{e.message}",
|
44
|
+
'CONNECT_ERROR'
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_env_vars(env_vars)
|
49
|
+
@env_vars = env_vars.each_with_object([]) do |env_var, acc|
|
50
|
+
acc << "--env"
|
51
|
+
acc << "#{env_var[0]}=#{Shellwords.shellescape(env_var[1])}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute(command)
|
56
|
+
lxc_command = %w[lxc exec]
|
57
|
+
lxc_command += @env_vars if @env_vars
|
58
|
+
lxc_command += %W[#{container_id} -- sh -c #{Shellwords.shellescape(command)}]
|
59
|
+
|
60
|
+
@logger.trace { "Executing: #{lxc_command.join(' ')}" }
|
61
|
+
Open3.popen3(lxc_command.join(' '))
|
62
|
+
end
|
63
|
+
|
64
|
+
private def execute_local_command(command)
|
65
|
+
Open3.capture3('lxc', *command, { binmode: true })
|
66
|
+
end
|
67
|
+
|
68
|
+
def upload_file(source, destination)
|
69
|
+
@logger.trace { "Uploading #{source} to #{destination}" }
|
70
|
+
args = %w[--create-dirs]
|
71
|
+
if File.directory?(source)
|
72
|
+
args << '--recursive'
|
73
|
+
# If we don't do this, LXD will upload to
|
74
|
+
# /tmp/d2020-11/d2020-11/dir instead of /tmp/d2020-11/dir
|
75
|
+
destination = Pathname.new(destination).dirname.to_s
|
76
|
+
end
|
77
|
+
cmd = %w[file push] + args + %W[#{source} #{container_id}#{destination}]
|
78
|
+
_out, err, stat = execute_local_command(cmd)
|
79
|
+
unless stat.exitstatus.zero?
|
80
|
+
raise "Error writing to #{container_id}: #{err}"
|
81
|
+
end
|
82
|
+
rescue StandardError => e
|
83
|
+
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
84
|
+
end
|
85
|
+
|
86
|
+
def download_file(source, destination, _download)
|
87
|
+
@logger.trace { "Downloading #{source} to #{destination}" }
|
88
|
+
FileUtils.mkdir_p(destination)
|
89
|
+
_out, err, stat = execute_local_command(%W[file pull --recursive #{container_id}#{source} #{destination}])
|
90
|
+
unless stat.exitstatus.zero?
|
91
|
+
raise "Error downloading content from container #{container_id}: #{err}"
|
92
|
+
end
|
93
|
+
rescue StandardError => e
|
94
|
+
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -59,6 +59,18 @@ module Bolt
|
|
59
59
|
# the result otherwise make sure an error is generated
|
60
60
|
if state == 'finished' || (result && result['_error'])
|
61
61
|
if result['_error']
|
62
|
+
unless result['_error'].is_a?(Hash)
|
63
|
+
result['_error'] = { 'kind' => 'puppetlabs.tasks/task-error',
|
64
|
+
'issue_code' => 'TASK_ERROR',
|
65
|
+
'msg' => result['_error'],
|
66
|
+
'details' => {} }
|
67
|
+
end
|
68
|
+
|
69
|
+
result['_error']['details'] ||= {}
|
70
|
+
unless result['_error']['details'].is_a?(Hash)
|
71
|
+
deets = result['_error']['details']
|
72
|
+
result['_error']['details'] = { 'msg' => deets }
|
73
|
+
end
|
62
74
|
file_line = %w[file line].zip(position).to_h.compact
|
63
75
|
result['_error']['details'].merge!(file_line) unless result['_error']['details']['file']
|
64
76
|
end
|
@@ -252,11 +264,7 @@ module Bolt
|
|
252
264
|
|
253
265
|
# If we get here, there's no error so we don't need the file or line
|
254
266
|
# number
|
255
|
-
Bolt::Result.for_command(target,
|
256
|
-
result.value['stdout'],
|
257
|
-
result.value['stderr'],
|
258
|
-
result.value['exit_code'],
|
259
|
-
action, obj, [])
|
267
|
+
Bolt::Result.for_command(target, result.value, action, obj, [])
|
260
268
|
end
|
261
269
|
end
|
262
270
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'bolt/transport/base'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
module Transport
|
9
|
+
class Podman < Docker
|
10
|
+
def with_connection(target)
|
11
|
+
conn = Connection.new(target)
|
12
|
+
conn.connect
|
13
|
+
yield conn
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'bolt/transport/podman/connection'
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logging'
|
4
|
+
require 'bolt/node/errors'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
module Transport
|
8
|
+
class Podman < Docker
|
9
|
+
class Connection < Connection
|
10
|
+
attr_reader :user, :target
|
11
|
+
|
12
|
+
def initialize(target)
|
13
|
+
raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
|
14
|
+
@target = target
|
15
|
+
@user = ENV['USER'] || Etc.getlogin
|
16
|
+
@logger = Bolt::Logger.logger(target.safe_name)
|
17
|
+
@container_info = {}
|
18
|
+
@logger.trace("Initializing podman connection to #{target.safe_name}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_cmd(cmd, env_vars)
|
22
|
+
Bolt::Util.exec_podman(cmd, env_vars)
|
23
|
+
end
|
24
|
+
|
25
|
+
def shell
|
26
|
+
@shell ||= if Bolt::Util.windows?
|
27
|
+
Bolt::Shell::Powershell.new(target, self)
|
28
|
+
else
|
29
|
+
Bolt::Shell::Bash.new(target, self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def connect
|
34
|
+
# We don't actually have a connection, but we do need to
|
35
|
+
# check that the container exists and is running.
|
36
|
+
ps = execute_local_json_command('ps')
|
37
|
+
container = Array(ps).find { |item|
|
38
|
+
item["ID"].to_s.eql?(@target.host) ||
|
39
|
+
item["Id"].to_s.start_with?(@target.host) ||
|
40
|
+
Array(item["Names"]).include?(@target.host)
|
41
|
+
}
|
42
|
+
raise "Could not find a container with name or ID matching '#{@target.host}'" if container.nil?
|
43
|
+
# Now find the indepth container information
|
44
|
+
id = container["ID"] || container["Id"]
|
45
|
+
output = execute_local_json_command('inspect', [id])
|
46
|
+
# Store the container information for later
|
47
|
+
@container_info = output.first
|
48
|
+
@logger.trace { "Opened session" }
|
49
|
+
true
|
50
|
+
rescue StandardError => e
|
51
|
+
raise Bolt::Node::ConnectError.new(
|
52
|
+
"Failed to connect to #{target.safe_name}: #{e.message}",
|
53
|
+
'CONNECT_ERROR'
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Executes a command inside the target container. This is called from the shell class.
|
58
|
+
#
|
59
|
+
# @param command [string] The command to run
|
60
|
+
def execute(command)
|
61
|
+
args = []
|
62
|
+
args += %w[--interactive]
|
63
|
+
args += %w[--tty] if target.options['tty']
|
64
|
+
args += @env_vars if @env_vars
|
65
|
+
|
66
|
+
if target.options['shell-command'] && !target.options['shell-command'].empty?
|
67
|
+
# escape any double quotes in command
|
68
|
+
command = command.gsub('"', '\"')
|
69
|
+
command = "#{target.options['shell-command']} \"#{command}\""
|
70
|
+
end
|
71
|
+
|
72
|
+
podman_command = %w[podman exec] + args + [container_id] + Shellwords.split(command)
|
73
|
+
@logger.trace { "Executing: #{podman_command.join(' ')}" }
|
74
|
+
|
75
|
+
Open3.popen3(*podman_command)
|
76
|
+
rescue StandardError
|
77
|
+
@logger.trace { "Command aborted" }
|
78
|
+
raise
|
79
|
+
end
|
80
|
+
|
81
|
+
# Converts the JSON encoded STDOUT string from the podman cli into ruby objects
|
82
|
+
#
|
83
|
+
# @param stdout [String] The string to convert
|
84
|
+
# @return [Object] Ruby object representation of the JSON string
|
85
|
+
private def extract_json(stdout)
|
86
|
+
# Podman renders the output in pretty JSON, which results in a newline
|
87
|
+
# appearing in the output before the closing bracket.
|
88
|
+
# should we only get a single line with no newline at all, we also
|
89
|
+
# assume it is a single minified JSON object
|
90
|
+
stdout.strip!
|
91
|
+
newline = stdout.index("\n") || -1
|
92
|
+
bracket = stdout.index('}') || -1
|
93
|
+
JSON.parse(stdout) if bracket > newline
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -230,7 +230,7 @@ module Bolt
|
|
230
230
|
end
|
231
231
|
[in_wr, out_rd, err_rd, th]
|
232
232
|
rescue Errno::EMFILE => e
|
233
|
-
msg = "#{e.message}. This
|
233
|
+
msg = "#{e.message}. This might be resolved by increasing your user limit "\
|
234
234
|
"with 'ulimit -n 1024'. See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
|
235
235
|
raise Bolt::Error.new(msg, 'bolt/too-many-files')
|
236
236
|
end
|
@@ -130,7 +130,7 @@ module Bolt
|
|
130
130
|
|
131
131
|
[inp, out_rd, err_rd, th]
|
132
132
|
rescue Errno::EMFILE => e
|
133
|
-
msg = "#{e.message}. This
|
133
|
+
msg = "#{e.message}. This might be resolved by increasing your user limit "\
|
134
134
|
"with 'ulimit -n 1024'. See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
|
135
135
|
raise Bolt::Error.new(msg, 'bolt/too-many-files')
|
136
136
|
rescue StandardError
|
data/lib/bolt/util.rb
CHANGED
@@ -77,6 +77,14 @@ module Bolt
|
|
77
77
|
File.exist?(path) ? read_yaml_hash(path, file_name) : {}
|
78
78
|
end
|
79
79
|
|
80
|
+
def first_runs_free
|
81
|
+
Bolt::Config.user_path + '.first_runs_free'
|
82
|
+
end
|
83
|
+
|
84
|
+
def first_run?
|
85
|
+
Bolt::Config.user_path && !File.exist?(first_runs_free)
|
86
|
+
end
|
87
|
+
|
80
88
|
# Accepts a path with either 'plans' or 'tasks' in it and determines
|
81
89
|
# the name of the module
|
82
90
|
def module_name(path)
|
@@ -324,6 +332,40 @@ module Bolt
|
|
324
332
|
end
|
325
333
|
end
|
326
334
|
|
335
|
+
# Executes a Docker CLI command. This is useful for running commands as
|
336
|
+
# part of this class without having to go through the `execute`
|
337
|
+
# function and manage pipes.
|
338
|
+
#
|
339
|
+
# @param cmd [String] The docker command and arguments to run
|
340
|
+
# e.g. 'cp <src> <dest>' for `docker cp <src> <dest>`
|
341
|
+
# @return [String, String, Process::Status] The output of the command: STDOUT, STDERR, Process Status
|
342
|
+
def exec_docker(cmd, env = {})
|
343
|
+
Open3.capture3(env, 'docker', *cmd, { binmode: true })
|
344
|
+
end
|
345
|
+
|
346
|
+
# Executes a Podman CLI command. This is useful for running commands as
|
347
|
+
# part of this class without having to go through the `execute`
|
348
|
+
# function and manage pipes.
|
349
|
+
#
|
350
|
+
# @param cmd [String] The podman command and arguments to run
|
351
|
+
# e.g. 'cp <src> <dest>' for `podman cp <src> <dest>`
|
352
|
+
# @return [String, String, Process::Status] The output of the command: STDOUT, STDERR, Process Status
|
353
|
+
def exec_podman(cmd, env = {})
|
354
|
+
Open3.capture3(env, 'podman', *cmd, { binmode: true })
|
355
|
+
end
|
356
|
+
|
357
|
+
# Formats a map of environment variables to be passed to a command that
|
358
|
+
# accepts repeated `--env` flags
|
359
|
+
#
|
360
|
+
# @param env_vars [Hash] A map of environment variables keys and their values
|
361
|
+
# @return [String]
|
362
|
+
def format_env_vars_for_cli(env_vars)
|
363
|
+
@env_vars = env_vars.each_with_object([]) do |(key, value), acc|
|
364
|
+
acc << "--env"
|
365
|
+
acc << "#{key}=#{value}"
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
327
369
|
def unix_basename(path)
|
328
370
|
raise Bolt::ValidationError, "path must be a String, received #{path.class} #{path}" unless path.is_a?(String)
|
329
371
|
path.split('/').last
|
data/lib/bolt/version.rb
CHANGED
@@ -133,7 +133,14 @@ module BoltServer
|
|
133
133
|
task_data = body['task']
|
134
134
|
task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
|
135
135
|
parameters = body['parameters'] || {}
|
136
|
-
|
136
|
+
task_result = @executor.run_task(target, task, parameters)
|
137
|
+
task_result.each do |result|
|
138
|
+
value = result.value
|
139
|
+
next unless value.is_a?(Hash)
|
140
|
+
next unless value.key?('_sensitive')
|
141
|
+
value['_sensitive'] = value['_sensitive'].unwrap
|
142
|
+
end
|
143
|
+
[task_result, nil]
|
137
144
|
end
|
138
145
|
|
139
146
|
def run_command(target, body)
|
@@ -275,15 +282,19 @@ module BoltServer
|
|
275
282
|
Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
|
276
283
|
end
|
277
284
|
|
285
|
+
def pal_from_project_bolt_config(bolt_config)
|
286
|
+
modulepath_object = Bolt::Config::Modulepath.new(
|
287
|
+
bolt_config.modulepath,
|
288
|
+
boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH],
|
289
|
+
builtin_content_path: @config['builtin-content-dir']
|
290
|
+
)
|
291
|
+
Bolt::PAL.new(modulepath_object, nil, nil, nil, nil, nil, bolt_config.project)
|
292
|
+
end
|
293
|
+
|
278
294
|
def in_bolt_project(versioned_project)
|
279
295
|
@pal_mutex.synchronize do
|
280
296
|
bolt_config = config_from_project(versioned_project)
|
281
|
-
|
282
|
-
bolt_config.modulepath,
|
283
|
-
boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH],
|
284
|
-
builtin_content_path: @config['builtin-content-dir']
|
285
|
-
)
|
286
|
-
pal = Bolt::PAL.new(modulepath_object, nil, nil, nil, nil, nil, bolt_config.project)
|
297
|
+
pal = pal_from_project_bolt_config(bolt_config)
|
287
298
|
context = {
|
288
299
|
pal: pal,
|
289
300
|
config: bolt_config
|
@@ -351,8 +362,8 @@ module BoltServer
|
|
351
362
|
}
|
352
363
|
end
|
353
364
|
|
354
|
-
def allowed_helper(metadata, allowlist)
|
355
|
-
allowed =
|
365
|
+
def allowed_helper(pal, metadata, allowlist)
|
366
|
+
allowed = !pal.filter_content([metadata['name']], allowlist).empty?
|
356
367
|
metadata.merge({ 'allowed' => allowed })
|
357
368
|
end
|
358
369
|
|
@@ -366,21 +377,27 @@ module BoltServer
|
|
366
377
|
plans.map { |plan_name| { 'name' => plan_name } }
|
367
378
|
end
|
368
379
|
|
369
|
-
def file_metadatas(
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
metadata = Puppet::FileServing::Metadata.new(base_path, relative_path: relative_file_path)
|
378
|
-
metadata.checksum_type = 'sha256'
|
379
|
-
metadata.links = 'follow'
|
380
|
-
metadata.collect
|
381
|
-
metadata.to_data_hash
|
380
|
+
def file_metadatas(versioned_project, module_name, file)
|
381
|
+
abs_file_path = @pal_mutex.synchronize do
|
382
|
+
bolt_config = config_from_project(versioned_project)
|
383
|
+
pal = pal_from_project_bolt_config(bolt_config)
|
384
|
+
pal.in_bolt_compiler do
|
385
|
+
mod = Puppet.lookup(:current_environment).module(module_name)
|
386
|
+
raise ArgumentError, "`module_name`: #{module_name} does not exist" unless mod
|
387
|
+
mod.file(file)
|
382
388
|
end
|
383
389
|
end
|
390
|
+
|
391
|
+
raise ArgumentError, "`file`: #{file} does not exist inside the module's 'files' directory" unless abs_file_path
|
392
|
+
|
393
|
+
fileset = Puppet::FileServing::Fileset.new(abs_file_path, 'recurse' => 'yes')
|
394
|
+
Puppet::FileServing::Fileset.merge(fileset).collect do |relative_file_path, base_path|
|
395
|
+
metadata = Puppet::FileServing::Metadata.new(base_path, relative_path: relative_file_path)
|
396
|
+
metadata.checksum_type = 'sha256'
|
397
|
+
metadata.links = 'follow'
|
398
|
+
metadata.collect
|
399
|
+
metadata.to_data_hash
|
400
|
+
end
|
384
401
|
end
|
385
402
|
|
386
403
|
get '/' do
|
@@ -520,7 +537,7 @@ module BoltServer
|
|
520
537
|
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
521
538
|
in_bolt_project(params['versioned_project']) do |context|
|
522
539
|
plan_info = pe_plan_info(context[:pal], params[:module_name], params[:plan_name])
|
523
|
-
plan_info = allowed_helper(plan_info, context[:config].project.plans)
|
540
|
+
plan_info = allowed_helper(context[:pal], plan_info, context[:config].project.plans)
|
524
541
|
[200, plan_info.to_json]
|
525
542
|
end
|
526
543
|
rescue Bolt::Error => e
|
@@ -550,7 +567,7 @@ module BoltServer
|
|
550
567
|
'versioned_project' => params['versioned_project']
|
551
568
|
}
|
552
569
|
task_info = pe_task_info(context[:pal], params[:module_name], params[:task_name], ps_parameters)
|
553
|
-
task_info = allowed_helper(task_info, context[:config].project.tasks)
|
570
|
+
task_info = allowed_helper(context[:pal], task_info, context[:config].project.tasks)
|
554
571
|
[200, task_info.to_json]
|
555
572
|
end
|
556
573
|
rescue Bolt::Error => e
|
@@ -590,7 +607,7 @@ module BoltServer
|
|
590
607
|
plans_response = plan_list(context[:pal])
|
591
608
|
|
592
609
|
# Dig in context for the allowlist of plans from project object
|
593
|
-
plans_response.map! { |metadata| allowed_helper(metadata, context[:config].project.plans) }
|
610
|
+
plans_response.map! { |metadata| allowed_helper(context[:pal], metadata, context[:config].project.plans) }
|
594
611
|
|
595
612
|
# We structure this array of plans to be an array of hashes so that it matches the structure
|
596
613
|
# returned by the puppetserver API that serves data like this. Structuring the output this way
|
@@ -626,7 +643,7 @@ module BoltServer
|
|
626
643
|
tasks_response = task_list(context[:pal])
|
627
644
|
|
628
645
|
# Dig in context for the allowlist of tasks from project object
|
629
|
-
tasks_response.map! { |metadata| allowed_helper(metadata, context[:config].project.tasks) }
|
646
|
+
tasks_response.map! { |metadata| allowed_helper(context[:pal], metadata, context[:config].project.tasks) }
|
630
647
|
|
631
648
|
# We structure this array of tasks to be an array of hashes so that it matches the structure
|
632
649
|
# returned by the puppetserver API that serves data like this. Structuring the output this way
|
@@ -642,12 +659,11 @@ module BoltServer
|
|
642
659
|
#
|
643
660
|
# @param versioned_project [String] the versioned_project to fetch the file metadatas from
|
644
661
|
get '/project_file_metadatas/:module_name/*' do
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
end
|
662
|
+
versioned_project = params['versioned_project']
|
663
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if versioned_project.nil?
|
664
|
+
file = params[:splat].first
|
665
|
+
metadatas = file_metadatas(versioned_project, params[:module_name], file)
|
666
|
+
[200, metadatas.to_json]
|
651
667
|
rescue Bolt::Error => e
|
652
668
|
[400, e.to_json]
|
653
669
|
rescue ArgumentError => e
|
@@ -674,11 +690,26 @@ module BoltServer
|
|
674
690
|
Bolt::Util.validate_file('inventory file', context[:config].project.inventory_file)
|
675
691
|
|
676
692
|
begin
|
693
|
+
# Set the default puppet_library plugin hook if it has not already been
|
694
|
+
# set
|
695
|
+
context[:config].data['plugin-hooks']['puppet_library'] ||= {
|
696
|
+
'plugin' => 'task',
|
697
|
+
'task' => 'puppet_agent::install',
|
698
|
+
'parameters' => {
|
699
|
+
'stop_service' => true
|
700
|
+
}
|
701
|
+
}
|
702
|
+
|
677
703
|
connect_plugin = BoltServer::Plugin::PuppetConnectData.new(body['puppet_connect_data'])
|
678
704
|
plugins = Bolt::Plugin.setup(context[:config], context[:pal], load_plugins: false)
|
679
705
|
plugins.add_plugin(connect_plugin)
|
706
|
+
%w[aws_inventory azure_inventory gcloud_inventory].each do |plugin_name|
|
707
|
+
plugins.add_module_plugin(plugin_name) if plugins.known_plugin?(plugin_name)
|
708
|
+
end
|
680
709
|
inventory = Bolt::Inventory.from_config(context[:config], plugins)
|
681
|
-
target_list = inventory.get_targets('all').map
|
710
|
+
target_list = inventory.get_targets('all').map do |targ|
|
711
|
+
targ.to_h.merge({ 'transport' => targ.transport, 'plugin_hooks' => targ.plugin_hooks })
|
712
|
+
end
|
682
713
|
rescue Bolt::Plugin::PluginError::LoadingDisabled => e
|
683
714
|
msg = "Cannot load plugin #{e.details['plugin_name']}: plugin not supported"
|
684
715
|
raise BoltServer::Plugin::PluginNotSupported.new(msg, e.details['plugin_name'])
|