bolt 3.0.0 → 3.5.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.

Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +13 -11
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  5. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
  9. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  10. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  11. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
  12. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  14. data/lib/bolt/apply_result.rb +1 -1
  15. data/lib/bolt/bolt_option_parser.rb +6 -3
  16. data/lib/bolt/cli.rb +96 -16
  17. data/lib/bolt/config.rb +4 -0
  18. data/lib/bolt/config/options.rb +21 -3
  19. data/lib/bolt/config/transport/lxd.rb +23 -0
  20. data/lib/bolt/config/transport/options.rb +8 -1
  21. data/lib/bolt/container_result.rb +105 -0
  22. data/lib/bolt/error.rb +15 -0
  23. data/lib/bolt/executor.rb +22 -7
  24. data/lib/bolt/inventory/options.rb +9 -0
  25. data/lib/bolt/inventory/target.rb +16 -0
  26. data/lib/bolt/logger.rb +8 -0
  27. data/lib/bolt/module_installer.rb +2 -2
  28. data/lib/bolt/module_installer/puppetfile.rb +2 -2
  29. data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
  30. data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
  31. data/lib/bolt/node/output.rb +14 -4
  32. data/lib/bolt/outputter/human.rb +106 -23
  33. data/lib/bolt/outputter/logger.rb +17 -0
  34. data/lib/bolt/pal.rb +25 -4
  35. data/lib/bolt/pal/yaml_plan.rb +1 -2
  36. data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
  37. data/lib/bolt/pal/yaml_plan/step.rb +91 -31
  38. data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
  39. data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
  40. data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
  41. data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
  42. data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
  43. data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
  44. data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
  45. data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
  46. data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
  47. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  48. data/lib/bolt/plan_creator.rb +1 -1
  49. data/lib/bolt/project_manager.rb +1 -1
  50. data/lib/bolt/project_manager/module_migrator.rb +1 -1
  51. data/lib/bolt/result.rb +11 -15
  52. data/lib/bolt/shell.rb +16 -0
  53. data/lib/bolt/shell/bash.rb +61 -31
  54. data/lib/bolt/shell/bash/tmpdir.rb +2 -2
  55. data/lib/bolt/shell/powershell.rb +34 -12
  56. data/lib/bolt/shell/powershell/snippets.rb +30 -3
  57. data/lib/bolt/task.rb +1 -1
  58. data/lib/bolt/transport/base.rb +0 -9
  59. data/lib/bolt/transport/docker.rb +1 -125
  60. data/lib/bolt/transport/docker/connection.rb +77 -167
  61. data/lib/bolt/transport/lxd.rb +26 -0
  62. data/lib/bolt/transport/lxd/connection.rb +99 -0
  63. data/lib/bolt/transport/orch.rb +13 -5
  64. data/lib/bolt/transport/ssh/connection.rb +1 -1
  65. data/lib/bolt/transport/winrm/connection.rb +1 -1
  66. data/lib/bolt/util.rb +31 -0
  67. data/lib/bolt/version.rb +1 -1
  68. data/lib/bolt_server/transport_app.rb +61 -33
  69. data/lib/bolt_spec/bolt_context.rb +9 -4
  70. data/lib/bolt_spec/plans.rb +1 -109
  71. data/lib/bolt_spec/plans/action_stubs.rb +1 -1
  72. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  73. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  74. data/lib/bolt_spec/plans/mock_executor.rb +90 -7
  75. data/modules/puppet_connect/plans/test_input_data.pp +65 -7
  76. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e265544847cf9e2086949db52ee887dc05e8b0aab953728742982f9cd8965779
4
- data.tar.gz: 616156a11d84daa9b28328b1c19f6c5bc23ca6036741c4898cc7dc9e891ca81d
3
+ metadata.gz: 41eb3934a2ddce5d1021b66f5aca6eccfdb91bdedd5dc08cfdc1cfb4664f91e8
4
+ data.tar.gz: 8a04142fd9de53e8812cff0c540098a8548a8b18245e300835896b457a86d348
5
5
  SHA512:
6
- metadata.gz: bc271dc6f2e7dd536ff60b4af94f48757fda48794808f4a2ea055a064c6627c01f832c3a64a430927879206b8aa1aaee94ac7075caed65580a06fa3402304bb4
7
- data.tar.gz: 6ca3af874edf3c165766c8a48070b130defb611d0fa98ee92393bf44e524182341a8812c3730c3d4094aecbb99d7fd52201b9cf6f6e262afa75849042737cdde
6
+ metadata.gz: 24bc37edf14e9c5d3ef1b8df5d5ed9dd0161ebb44da96e0b2777703c5b71182179ebed740e5d67c04bad41b9ecdb6e0a40d5fcb5ec08eb59171da36a3d5a3756
7
+ data.tar.gz: 0e5795469709939091282a51df744076e87e40e5dad0f5bc6395d0d7927dcbf5f788bccf23bd5c455c29232faa958ddcbe5130626eb82d056404f8cb1e088cdd
data/Puppetfile CHANGED
@@ -5,14 +5,14 @@ forge "http://forge.puppetlabs.com"
5
5
  moduledir File.join(File.dirname(__FILE__), 'modules')
6
6
 
7
7
  # Core modules used by 'apply'
8
- mod 'puppetlabs-service', '1.4.0'
9
- mod 'puppetlabs-puppet_agent', '4.4.0'
8
+ mod 'puppetlabs-service', '2.0.0'
9
+ mod 'puppetlabs-puppet_agent', '4.5.0'
10
10
  mod 'puppetlabs-facts', '1.4.0'
11
11
 
12
12
  # Core types and providers for Puppet 6
13
- mod 'puppetlabs-augeas_core', '1.1.1'
13
+ mod 'puppetlabs-augeas_core', '1.1.2'
14
14
  mod 'puppetlabs-host_core', '1.0.3'
15
- mod 'puppetlabs-scheduled_task', '2.3.1'
15
+ mod 'puppetlabs-scheduled_task', '3.0.0'
16
16
  mod 'puppetlabs-sshkeys_core', '2.2.0'
17
17
  mod 'puppetlabs-zfs_core', '1.2.0'
18
18
  mod 'puppetlabs-cron_core', '1.0.5'
@@ -22,19 +22,20 @@ mod 'puppetlabs-yumrepo_core', '1.0.7'
22
22
  mod 'puppetlabs-zone_core', '1.0.3'
23
23
 
24
24
  # Useful additional modules
25
- mod 'puppetlabs-package', '1.4.0'
26
- mod 'puppetlabs-puppet_conf', '0.8.0'
25
+ mod 'puppetlabs-package', '2.0.0'
26
+ mod 'puppetlabs-powershell_task_helper', '0.1.0'
27
+ mod 'puppetlabs-puppet_conf', '1.1.0'
27
28
  mod 'puppetlabs-python_task_helper', '0.5.0'
28
- mod 'puppetlabs-reboot', '3.2.0'
29
+ mod 'puppetlabs-reboot', '4.0.2'
29
30
  mod 'puppetlabs-ruby_task_helper', '0.6.0'
30
31
  mod 'puppetlabs-ruby_plugin_helper', '0.2.0'
31
- mod 'puppetlabs-stdlib', '6.6.0'
32
+ mod 'puppetlabs-stdlib', '7.0.0'
32
33
 
33
34
  # Plugin modules
34
- mod 'puppetlabs-aws_inventory', '0.6.0'
35
+ mod 'puppetlabs-aws_inventory', '0.7.0'
35
36
  mod 'puppetlabs-azure_inventory', '0.5.0'
36
- mod 'puppetlabs-gcloud_inventory', '0.2.0'
37
- mod 'puppetlabs-http_request', '0.2.1'
37
+ mod 'puppetlabs-gcloud_inventory', '0.3.0'
38
+ mod 'puppetlabs-http_request', '0.2.2'
38
39
  mod 'puppetlabs-pkcs7', '0.1.1'
39
40
  mod 'puppetlabs-secure_env_vars', '0.2.0'
40
41
  mod 'puppetlabs-terraform', '0.6.1'
@@ -45,3 +46,4 @@ mod 'puppetlabs-yaml', '0.2.0'
45
46
  mod 'canary', local: true
46
47
  mod 'aggregate', local: true
47
48
  mod 'puppetdb_fact', local: true
49
+ mod 'puppet_connect', local: true
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ Puppet::DataTypes.create_type('ContainerResult') do
4
+ interface <<-PUPPET
5
+ attributes => {
6
+ 'value' => Hash[String[1], Data],
7
+ },
8
+ functions => {
9
+ '[]' => Callable[[String[1]], Data],
10
+ error => Callable[[], Optional[Error]],
11
+ ok => Callable[[], Boolean],
12
+ status => Callable[[], String],
13
+ stdout => Callable[[], String],
14
+ stderr => Callable[[], String],
15
+ to_data => Callable[[], Hash]
16
+ }
17
+ PUPPET
18
+
19
+ load_file('bolt/container_result')
20
+
21
+ # Needed for Puppet to recognize Bolt::ContainerResult as a Puppet object when deserializing
22
+ Bolt::ContainerResult.include(Puppet::Pops::Types::PuppetObject)
23
+ implementation_class Bolt::ContainerResult
24
+ end
@@ -7,7 +7,7 @@ require 'bolt/error'
7
7
  # > **Note:** Not available in apply block
8
8
  Puppet::Functions.create_function(:add_facts) do
9
9
  # @param target A target.
10
- # @param facts A hash of fact names to values that may include structured facts.
10
+ # @param facts A hash of fact names to values that can include structured facts.
11
11
  # @return A `Target` object.
12
12
  # @example Adding facts to a target
13
13
  # add_facts($target, { 'os' => { 'family' => 'windows', 'name' => 'windows' } })
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bolt/error'
4
+ require 'json'
4
5
 
5
6
  # Runs a command on the given set of targets and returns the result from each command execution.
6
7
  # This function does nothing if the list of targets is empty.
@@ -13,7 +14,7 @@ Puppet::Functions.create_function(:run_command) do
13
14
  # @param options A hash of additional options.
14
15
  # @option options [Boolean] _catch_errors Whether to catch raised errors.
15
16
  # @option options [String] _run_as User to run as using privilege escalation.
16
- # @option options [Hash] _env_vars Map of environment variables to set
17
+ # @option options [Hash[String, Any]] _env_vars Map of environment variables to set
17
18
  # @return A list of results, one entry per target.
18
19
  # @example Run a command on targets
19
20
  # run_command('hostname', $targets, '_catch_errors' => true)
@@ -31,7 +32,7 @@ Puppet::Functions.create_function(:run_command) do
31
32
  # @param options A hash of additional options.
32
33
  # @option options [Boolean] _catch_errors Whether to catch raised errors.
33
34
  # @option options [String] _run_as User to run as using privilege escalation.
34
- # @option options [Hash] _env_vars Map of environment variables to set
35
+ # @option options [Hash[String, Any]] _env_vars Map of environment variables to set
35
36
  # @return A list of results, one entry per target.
36
37
  # @example Run a command on targets
37
38
  # run_command('hostname', $targets, 'Get hostname')
@@ -56,6 +57,23 @@ Puppet::Functions.create_function(:run_command) do
56
57
  options = options.transform_keys { |k| k.sub(/^_/, '').to_sym }
57
58
  options[:description] = description if description
58
59
 
60
+ # Ensure env_vars is a hash and that each hash value is transformed to JSON
61
+ # so we don't accidentally pass Ruby-style data to the target.
62
+ if options[:env_vars]
63
+ unless options[:env_vars].is_a?(Hash)
64
+ raise Bolt::ValidationError, "Option 'env_vars' must be a hash"
65
+ end
66
+
67
+ if (bad_keys = options[:env_vars].keys.reject { |k| k.is_a?(String) }).any?
68
+ raise Bolt::ValidationError,
69
+ "Keys for option 'env_vars' must be strings: #{bad_keys.map(&:inspect).join(', ')}"
70
+ end
71
+
72
+ options[:env_vars] = options[:env_vars].transform_values do |val|
73
+ [Array, Hash].include?(val.class) ? val.to_json : val
74
+ end
75
+ end
76
+
59
77
  executor = Puppet.lookup(:bolt_executor)
60
78
  inventory = Puppet.lookup(:bolt_inventory)
61
79
 
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/container_result'
4
+ require 'bolt/error'
5
+ require 'bolt/util'
6
+
7
+ # Run a container and return its output to stdout and stderr.
8
+ #
9
+ # > **Note:** Not available in apply block
10
+ Puppet::Functions.create_function(:run_container) do
11
+ # Run a container.
12
+ # @param image The name of the image to run.
13
+ # @param options A hash of additional options.
14
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
15
+ # @option options [String] cmd A command to run in the container.
16
+ # @option options [Hash[String, Data]] env_vars Map of environment variables to set.
17
+ # @option options [Hash[Integer, Integer]] ports A map of container ports to
18
+ # publish. Keys are the host port, values are the corresponding container
19
+ # port.
20
+ # @option options [Boolean] rm Whether to remove the container once it exits.
21
+ # @option options [Hash[String, String]] volumes A map of absolute paths on
22
+ # the host to absolute paths on the remote to mount.
23
+ # @option options [String] workdir The working directory within the container.
24
+ # @return Output from the container.
25
+ # @example Run Nginx proxy manager
26
+ # run_container('jc21/nginx-proxy-manager', 'ports' => { 80 => 80, 81 => 81, 443 => 443 })
27
+ dispatch :run_container do
28
+ param 'String[1]', :image
29
+ optional_param 'Hash[String[1], Any]', :options
30
+ return_type 'ContainerResult'
31
+ end
32
+
33
+ def run_container(image, options = {})
34
+ unless Puppet[:tasks]
35
+ raise Puppet::ParseErrorWithIssue
36
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'run_container')
37
+ end
38
+
39
+ # Send Analytics Report
40
+ executor = Puppet.lookup(:bolt_executor)
41
+ executor.report_function_call(self.class.name)
42
+
43
+ options = options.transform_keys { |k| k.sub(/^_/, '').to_sym }
44
+ validate_options(options)
45
+
46
+ if options.key?(:env_vars)
47
+ options[:env_vars] = options[:env_vars].transform_values do |val|
48
+ [Array, Hash].include?(val.class) ? val.to_json : val
49
+ end
50
+ end
51
+
52
+ if options[:ports]
53
+ ports = options[:ports].each_with_object([]) do |(host_port, container_port), acc|
54
+ acc << "-p"
55
+ acc << "#{host_port}:#{container_port}"
56
+ end
57
+ end
58
+
59
+ if options[:volumes]
60
+ volumes = options[:volumes].each_with_object([]) do |(host_path, remote_path), acc|
61
+ begin
62
+ FileUtils.mkdir_p(host_path)
63
+ rescue StandardError => e
64
+ message = "Unable to create host volume directory #{host_path}: #{e.message}"
65
+ raise Bolt::Error.new(message, 'bolt/file-error')
66
+ end
67
+ acc << "-v"
68
+ acc << "#{host_path}:#{remote_path}"
69
+ end
70
+ end
71
+
72
+ # Run the container
73
+ # `docker run` will automatically pull the image if it isn't already downloaded
74
+ cmd = %w[run]
75
+ cmd += Bolt::Util.format_env_vars_for_cli(options[:env_vars]) if options[:env_vars]
76
+ cmd += volumes if volumes
77
+ cmd += ports if ports
78
+ cmd << "--rm" if options[:rm]
79
+ cmd += %W[-w #{options[:workdir]}] if options[:workdir]
80
+ cmd << image
81
+ cmd += Shellwords.shellsplit(options[:cmd]) if options[:cmd]
82
+
83
+ executor.publish_event(type: :container_start, image: image)
84
+ out, err, status = Bolt::Util.exec_docker(cmd)
85
+
86
+ o = out.is_a?(String) ? out.dup.force_encoding('utf-8') : out
87
+ e = err.is_a?(String) ? err.dup.force_encoding('utf-8') : err
88
+
89
+ unless status.exitstatus.zero?
90
+ result = Bolt::ContainerResult.from_exception(e,
91
+ status.exitstatus,
92
+ image,
93
+ position: Puppet::Pops::PuppetStack.top_of_stack)
94
+ executor.publish_event(type: :container_finish, result: result)
95
+ if options[:catch_errors]
96
+ return result
97
+ else
98
+ raise Bolt::ContainerFailure, result
99
+ end
100
+ end
101
+
102
+ value = { 'stdout' => o, 'stderr' => e, 'exit_code' => status.exitstatus }
103
+ result = Bolt::ContainerResult.new(value, object: image)
104
+ executor.publish_event(type: :container_finish, result: result)
105
+ result
106
+ end
107
+
108
+ def validate_options(options)
109
+ if options.key?(:env_vars)
110
+ ev = options[:env_vars]
111
+ unless ev.is_a?(Hash)
112
+ msg = "Option 'env_vars' must be a hash. Received #{ev} which is a #{ev.class}"
113
+ raise Bolt::ValidationError, msg
114
+ end
115
+
116
+ if (bad_keys = ev.keys.reject { |k| k.is_a?(String) }).any?
117
+ msg = "Keys for option 'env_vars' must be strings: #{bad_keys.map(&:inspect).join(', ')}"
118
+ raise Bolt::ValidationError, msg
119
+ end
120
+ end
121
+
122
+ if options.key?(:volumes)
123
+ volumes = options[:volumes]
124
+ unless volumes.is_a?(Hash)
125
+ msg = "Option 'volumes' must be a hash. Received #{volumes} which is a #{volumes.class}"
126
+ raise Bolt::ValidationError, msg
127
+ end
128
+
129
+ if (bad_vs = volumes.reject { |k, v| k.is_a?(String) && v.is_a?(String) }).any?
130
+ msg = "Option 'volumes' only accepts strings for keys and values. "\
131
+ "Received: #{bad_vs.map(&:inspect).join(', ')}"
132
+ raise Bolt::ValidationError, msg
133
+ end
134
+ end
135
+
136
+ if options.key?(:cmd) && !options[:cmd].is_a?(String)
137
+ cmd = options[:cmd]
138
+ msg = "Option 'cmd' must be a string. Received #{cmd} which is a #{cmd.class}"
139
+ raise Bolt::ValidationError, msg
140
+ end
141
+
142
+ if options.key?(:workdir) && !options[:workdir].is_a?(String)
143
+ wd = options[:workdir]
144
+ msg = "Option 'workdir' must be a string. Received #{wd} which is a #{wd.class}"
145
+ raise Bolt::ValidationError, msg
146
+ end
147
+
148
+ if options.key?(:ports)
149
+ ports = options[:ports]
150
+ unless ports.is_a?(Hash)
151
+ msg = "Option 'ports' must be a hash. Received #{ports} which is a #{ports.class}"
152
+ raise Bolt::ValidationError, msg
153
+ end
154
+
155
+ if (bad_ps = ports.reject { |k, v| k.is_a?(Integer) && v.is_a?(Integer) }).any?
156
+ msg = "Option 'ports' only accepts integers for keys and values. "\
157
+ "Received: #{bad_ps.map(&:inspect).join(', ')}"
158
+ raise Bolt::ValidationError, msg
159
+ end
160
+ end
161
+ end
162
+ end
@@ -256,7 +256,7 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
256
256
  if nodes_param
257
257
  if params['nodes']
258
258
  raise ArgumentError,
259
- "A plan's 'nodes' parameter may be specified as the second positional argument to " \
259
+ "A plan's 'nodes' parameter can be specified as the second positional argument to " \
260
260
  "run_plan(), but in that case 'nodes' must not be specified in the named arguments " \
261
261
  "hash."
262
262
  end
@@ -265,7 +265,7 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
265
265
  elsif targets_param
266
266
  if params['targets']
267
267
  raise ArgumentError,
268
- "A plan's 'targets' parameter may be specified as the second positional argument to " \
268
+ "A plan's 'targets' parameter can be specified as the second positional argument to " \
269
269
  "run_plan(), but in that case 'targets' must not be specified in the named arguments " \
270
270
  "hash."
271
271
  end
@@ -6,19 +6,24 @@
6
6
  # > **Note:** Not available in apply block
7
7
  Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFunction) do
8
8
  # Run a script.
9
- # @param script Path to a script to run on target. May be an absolute path or a modulename/filename selector for a
9
+ # @param script Path to a script to run on target. Can be an absolute path or a modulename/filename selector for a
10
10
  # file in $MODULEROOT/files.
11
11
  # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
12
12
  # @param options A hash of additional options.
13
13
  # @option options [Array[String]] arguments An array of arguments to be passed to the script.
14
+ # Cannot be used with `pwsh_params`.
15
+ # @option options [Hash] pwsh_params Map of named parameters to pass to a PowerShell script.
16
+ # Cannot be used with `arguments`.
14
17
  # @option options [Boolean] _catch_errors Whether to catch raised errors.
15
18
  # @option options [String] _run_as User to run as using privilege escalation.
16
- # @option options [Hash] _env_vars Map of environment variables to set
19
+ # @option options [Hash[String, Any]] _env_vars Map of environment variables to set.
17
20
  # @return A list of results, one entry per target.
18
21
  # @example Run a local script on Linux targets as 'root'
19
22
  # run_script('/var/tmp/myscript', $targets, '_run_as' => 'root')
20
23
  # @example Run a module-provided script with arguments
21
24
  # run_script('iis/setup.ps1', $target, 'arguments' => ['/u', 'Administrator'])
25
+ # @example Pass named parameters to a PowerShell script
26
+ # run_script('iis/setup.ps1', $target, 'pwsh_params' => { 'User' => 'Administrator' })
22
27
  dispatch :run_script do
23
28
  scope_param
24
29
  param 'String[1]', :script
@@ -28,15 +33,18 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
28
33
  end
29
34
 
30
35
  # Run a script, logging the provided description.
31
- # @param script Path to a script to run on target. May be an absolute path or a modulename/filename selector for a
36
+ # @param script Path to a script to run on target. Can be an absolute path or a modulename/filename selector for a
32
37
  # file in $MODULEROOT/files.
33
38
  # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
34
39
  # @param description A description to be output when calling this function.
35
40
  # @param options A hash of additional options.
36
41
  # @option options [Array[String]] arguments An array of arguments to be passed to the script.
42
+ # Cannot be used with `pwsh_params`.
43
+ # @option options [Hash] pwsh_params Map of named parameters to pass to a PowerShell script.
44
+ # Cannot be used with `arguments`.
37
45
  # @option options [Boolean] _catch_errors Whether to catch raised errors.
38
46
  # @option options [String] _run_as User to run as using privilege escalation.
39
- # @option options [Hash] _env_vars Map of environment variables to set
47
+ # @option options [Hash[String, Any]] _env_vars Map of environment variables to set.
40
48
  # @return A list of results, one entry per target.
41
49
  # @example Run a script
42
50
  # run_script('/var/tmp/myscript', $targets, 'Downloading my application')
@@ -59,9 +67,40 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
59
67
  .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'run_script')
60
68
  end
61
69
 
70
+ if options.key?('arguments') && options.key?('pwsh_params')
71
+ raise Bolt::ValidationError, "Cannot specify both 'arguments' and 'pwsh_params'"
72
+ end
73
+
74
+ if options.key?('pwsh_params') && !options['pwsh_params'].is_a?(Hash)
75
+ raise Bolt::ValidationError, "Option 'pwsh_params' must be a hash"
76
+ end
77
+
78
+ if options.key?('arguments') && !options['arguments'].is_a?(Array)
79
+ raise Bolt::ValidationError, "Option 'arguments' must be an array"
80
+ end
81
+
62
82
  arguments = options['arguments'] || []
83
+ pwsh_params = options['pwsh_params']
63
84
  options = options.select { |opt| opt.start_with?('_') }.transform_keys { |k| k.sub(/^_/, '').to_sym }
64
85
  options[:description] = description if description
86
+ options[:pwsh_params] = pwsh_params if pwsh_params
87
+
88
+ # Ensure env_vars is a hash and that each hash value is transformed to JSON
89
+ # so we don't accidentally pass Ruby-style data to the target.
90
+ if options[:env_vars]
91
+ unless options[:env_vars].is_a?(Hash)
92
+ raise Bolt::ValidationError, "Option 'env_vars' must be a hash"
93
+ end
94
+
95
+ if (bad_keys = options[:env_vars].keys.reject { |k| k.is_a?(String) }).any?
96
+ raise Bolt::ValidationError,
97
+ "Keys for option 'env_vars' must be strings: #{bad_keys.map(&:inspect).join(', ')}"
98
+ end
99
+
100
+ options[:env_vars] = options[:env_vars].transform_values do |val|
101
+ [Array, Hash].include?(val.class) ? val.to_json : val
102
+ end
103
+ end
65
104
 
66
105
  executor = Puppet.lookup(:bolt_executor)
67
106
  inventory = Puppet.lookup(:bolt_inventory)
@@ -80,7 +119,7 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
80
119
  Puppet::Pops::Issues::NOT_A_FILE, file: script
81
120
  )
82
121
  end
83
-
122
+ executor.report_file_source(self.class.name, script)
84
123
  # Ensure that given targets are all Target instances)
85
124
  targets = inventory.get_targets(targets)
86
125
 
@@ -76,7 +76,7 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
76
76
  Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: source
77
77
  )
78
78
  end
79
-
79
+ executor.report_file_source(self.class.name, source)
80
80
  # Ensure that that given targets are all Target instances
81
81
  targets = inventory.get_targets(targets)
82
82
  if targets.empty?
@@ -12,5 +12,6 @@ type Boltlib::PlanResult = Variant[Boolean,
12
12
  ResultSet,
13
13
  Target,
14
14
  ResourceInstance,
15
+ ContainerResult,
15
16
  Array[Boltlib::PlanResult],
16
17
  Hash[String, Boltlib::PlanResult]]
@@ -17,7 +17,8 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
17
17
 
18
18
  def read(scope, filename)
19
19
  # Send Analytics Report
20
- Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
20
+ executor = Puppet.lookup(:bolt_executor) {}
21
+ executor&.report_function_call(self.class.name)
21
22
 
22
23
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
23
24
  unless found && Puppet::FileSystem.exist?(found)
@@ -25,7 +26,7 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
25
26
  Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: filename
26
27
  )
27
28
  end
28
-
29
+ executor&.report_file_source(self.class.name, filename)
29
30
  File.read(found)
30
31
  end
31
32
  end