bolt 1.45.0 → 1.47.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28b4c5859fab6ec57cac9d5ad791434aabfea666711f773f307d296df25274a9
4
- data.tar.gz: 8dceb70532864e93b15c9237fff7ef803dba83650519f54dc60bbbce7e85a991
3
+ metadata.gz: 8d8c51d1ab9354303ccda2f36ebd31553fa0d8bf91adfe2e505e701cb8fc32eb
4
+ data.tar.gz: f092a00fc4ea73fea86b54e6d36f535e873a15d79d00bc85bae433b5fa6eace5
5
5
  SHA512:
6
- metadata.gz: 88716d23b4acaf2bf97fa73dbd96d802c185591bc1ffa1115268c1c27362be35e6a3e94e8f9a75c3d65e437c5aaf176776a2789ce2c9d7cbeeddd3db88496659
7
- data.tar.gz: 8875843ed41a1cba383997f5fbc7e865ae0f8e0a401e84196d9339fd9fdde58652ca99128ed1527a61afa46fbb526abbf13dafd67f22729b14db5739a176f53c
6
+ metadata.gz: 741f8a885261e3b50413201fcfb0688d5a0c39a593a6225e9472c2822a114ae135be3b3fb30ee0433d0572ec4440f6cc0f505e10727098d55613b722fd9d86aa
7
+ data.tar.gz: 3bce21f684265bd81f23b2a182c6d3bdc858a742faa28591a2483836dea04c736282d0e3551cd54843c7276d85d2e27e1f0794e956a7d973f57493e18973311a
@@ -4,6 +4,7 @@ require 'etc'
4
4
  require 'logging'
5
5
  require 'pathname'
6
6
  require 'bolt/boltdir'
7
+ require 'bolt/logger'
7
8
  require 'bolt/transport/ssh'
8
9
  require 'bolt/transport/winrm'
9
10
  require 'bolt/transport/orch'
@@ -36,11 +37,69 @@ module Bolt
36
37
  :puppetfile_config, :plugins, :plugin_hooks, :future, :trusted_external
37
38
  attr_writer :modulepath
38
39
 
39
- TRANSPORT_OPTIONS = %i[password run-as sudo-password extensions sudo-executable
40
- private-key tty tmpdir user connect-timeout disconnect-timeout
41
- cacert token-file service-url interpreters file-protocol smb-port realm].freeze
42
-
43
- PUPPETFILE_OPTIONS = %w[proxy forge].freeze
40
+ OPTIONS = {
41
+ "color" => "Whether to use colored output when printing messages to the console.",
42
+ "compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
43
+ "concurrency" => "The number of threads to use when executing on remote targets.",
44
+ "format" => "The format to use when printing results. Options are `human` and `json`.",
45
+ "hiera-config" => "The path to your Hiera config.",
46
+ "interpreters" => "A map of an extension name to the absolute path of an executable, "\
47
+ "enabling you to override the shebang defined in a task executable. The "\
48
+ "extension can optionally be specified with the `.` character (`.py` and "\
49
+ "`py` both map to a task executable `task.py`) and the extension is case "\
50
+ "sensitive. The transports that support interpreter configuration are "\
51
+ "`docker`, `local`, `ssh`, and `winrm`. When a target's name is `localhost`, "\
52
+ "Ruby tasks run with the Bolt Ruby interpreter by default.",
53
+ "inventoryfile" => "The path to a structured data inventory file used to refer to groups of "\
54
+ "targets on the command line and from plans.",
55
+ "log" => "The configuration of the logfile output. Configuration can be set for "\
56
+ "`console` and the path to a log file, such as `~/.puppetlabs/bolt/debug.log`.",
57
+ "modulepath" => "The module path for loading tasks and plan code. This is either an array "\
58
+ "of directories or a string containing a list of directories separated by the "\
59
+ "OS-specific PATH separator.",
60
+ "plugin_hooks" => "Which plugins a specific hook should use.",
61
+ "puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
62
+ "save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
63
+ "your target names include passwords, set this value to `false` to avoid "\
64
+ "writing passwords to disk.",
65
+ "transport" => "The default transport to use when the transport for a target is not "\
66
+ "specified in the URL or inventory.",
67
+ "trusted-external-command" => "The path to an executable on the Bolt controller that can produce "\
68
+ "external trusted facts. **External trusted facts are experimental in both "\
69
+ "Puppet and Bolt and this API may change or be removed.**",
70
+ "future" => "Whether to use new, breaking changes. This allows testing if Bolt content "\
71
+ "is compatible with expected future behavior."
72
+ }.freeze
73
+
74
+ DEFAULT_OPTIONS = {
75
+ "color" => true,
76
+ "concurrency" => 100,
77
+ "compile-concurrency" => "Number of cores",
78
+ "format" => "human",
79
+ "hiera-config" => "Boltdir/hiera.yaml",
80
+ "inventoryfile" => "Boltdir/inventory.yaml",
81
+ "modulepath" => ["Boltdir/modules", "Boltdir/site-modules", "Boltdir/site"],
82
+ "save-rerun" => true,
83
+ "future" => false
84
+ }.freeze
85
+
86
+ PUPPETFILE_OPTIONS = {
87
+ "forge" => "A subsection that can have its own `proxy` setting to set an HTTP proxy for Forge operations "\
88
+ "only, and a `baseurl` setting to specify a different Forge host.",
89
+ "proxy" => "The HTTP proxy to use for Git and Forge operations."
90
+ }.freeze
91
+
92
+ LOG_OPTIONS = {
93
+ "append" => "Add output to an existing log file. Available only for logs output to a "\
94
+ "filepath.",
95
+ "level" => "The type of information in the log. Either `debug`, `info`, `notice`, "\
96
+ "`warn`, or `error`."
97
+ }.freeze
98
+
99
+ DEFAULT_LOG_OPTIONS = {
100
+ "append" => true,
101
+ "level" => "`warn` for console, `notice` for file"
102
+ }.freeze
44
103
 
45
104
  def self.default
46
105
  new(Bolt::Boltdir.new('.'), {})
@@ -194,8 +253,10 @@ module Bolt
194
253
  @compile_concurrency = options[:'compile-concurrency'] if options[:'compile-concurrency']
195
254
 
196
255
  TRANSPORTS.each_key do |transport|
256
+ # Get the options first since transport is modified in the next line
257
+ transport_options = TRANSPORTS[transport]::OPTIONS.keys.map(&:to_sym)
197
258
  transport = @transports[transport]
198
- TRANSPORT_OPTIONS.each do |key|
259
+ transport_options.each do |key|
199
260
  if options[key]
200
261
  transport[key.to_s] = Bolt::Util.walk_keys(options[key], &:to_s)
201
262
  end
@@ -96,12 +96,15 @@ module Bolt
96
96
  end
97
97
  end
98
98
 
99
- if result.message
100
- @stream.puts(remove_trail(indent(2, result.message)))
101
- end
99
+ # Only print results if there's something other than empty string and hash
100
+ if result.value.empty? || (result.value.keys == ['_output'] && !result.message?)
101
+ @stream.puts(indent(2, "#{result.action.capitalize} completed successfully with no result"))
102
+ else
103
+ # Only print messages that have something other than whitespace
104
+ if result.message?
105
+ @stream.puts(remove_trail(indent(2, result.message)))
106
+ end
102
107
 
103
- # There is more information to output
104
- if result.generic_value
105
108
  # Use special handling if the result looks like a command or script result
106
109
  if result.generic_value.keys == %w[stdout stderr exit_code]
107
110
  unless result['stdout'].strip.empty?
@@ -112,7 +115,7 @@ module Bolt
112
115
  @stream.puts(indent(2, "STDERR:"))
113
116
  @stream.puts(indent(4, result['stderr']))
114
117
  end
115
- else
118
+ elsif result.generic_value.any?
116
119
  @stream.puts(indent(2, ::JSON.pretty_generate(result.generic_value)))
117
120
  end
118
121
  end
@@ -142,7 +142,7 @@ module Bolt
142
142
  plugins
143
143
  end
144
144
 
145
- RUBY_PLUGINS = %w[install_agent task pkcs7 prompt].freeze
145
+ RUBY_PLUGINS = %w[task pkcs7 prompt].freeze
146
146
  BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory yaml].freeze
147
147
  DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
148
148
 
@@ -95,7 +95,6 @@ module Bolt
95
95
  @value = value || {}
96
96
  @action = action
97
97
  @object = object
98
- @value_set = !value.nil?
99
98
  if error && !error.is_a?(Hash)
100
99
  raise "TODO: how did we get a string error"
101
100
  end
@@ -107,6 +106,10 @@ module Bolt
107
106
  @value['_output']
108
107
  end
109
108
 
109
+ def message?
110
+ message && !message.strip.empty?
111
+ end
112
+
110
113
  def status_hash
111
114
  # DEPRECATION: node in status hashes is deprecated and should be removed in 2.0
112
115
  base = {
@@ -121,9 +124,7 @@ module Bolt
121
124
  end
122
125
 
123
126
  def generic_value
124
- if @value_set
125
- value.reject { |k, _| %w[_error _output].include? k }
126
- end
127
+ value.reject { |k, _| %w[_error _output].include? k }
127
128
  end
128
129
 
129
130
  def eql?(other)
@@ -157,9 +157,37 @@ module Bolt
157
157
 
158
158
  # Satisfies the Puppet datatypes API
159
159
  def self.from_asserted_hash(hash)
160
+ if hash['uri'] && hash['options']
161
+ logger = Logging.logger[self]
162
+ msg = <<~MSG
163
+ #{Puppet::Pops::PuppetStack.top_of_stack.join(':')}
164
+ Deprecation Warning: Starting with Bolt 2.0, using 'Target.new' with an 'options' hash key will no
165
+ will no longer be supported. Use 'Target.new(<config>)', where 'config' is a hash with the same
166
+ structure used to define targets in the inventory V2 file. For more information see
167
+ https://puppet.com/docs/bolt/latest/writing_plans.html#creating-target-objects
168
+ MSG
169
+ logger.warn(msg)
170
+ end
171
+
160
172
  new(hash['uri'], hash['options'])
161
173
  end
162
174
 
175
+ def self.from_asserted_args(uri, options = nil)
176
+ if options
177
+ logger = Logging.logger[self]
178
+ msg = <<~MSG
179
+ #{Puppet::Pops::PuppetStack.top_of_stack.join(':')}
180
+ Deprecation Warning: Starting with Bolt 2.0, 'Target.new(<uri>, <options>)' will no
181
+ longer be supported. Use 'Target.new(<config>)', where 'config' is a hash with the same
182
+ structure used to define targets in the inventory V2 file. For more information see
183
+ https://puppet.com/docs/bolt/latest/writing_plans.html#creating-target-objects
184
+ MSG
185
+ logger.warn(msg)
186
+ end
187
+
188
+ new(uri, options)
189
+ end
190
+
163
191
  # URI can be passes as nil
164
192
  def initialize(uri, options = nil)
165
193
  # lazy-load expensive gem code
@@ -7,8 +7,22 @@ require 'bolt/transport/base'
7
7
  module Bolt
8
8
  module Transport
9
9
  class Docker < Base
10
+ OPTIONS = {
11
+ "host" => "Host name.",
12
+ "interpreters" => "A map of an extension name to the absolute path of an executable, "\
13
+ "enabling you to override the shebang defined in a task executable. The "\
14
+ "extension can optionally be specified with the `.` character (`.py` and "\
15
+ "`py` both map to a task executable `task.py`) and the extension is case "\
16
+ "sensitive. When a target's name is `localhost`, Ruby tasks run with the "\
17
+ "Bolt Ruby interpreter by default.",
18
+ "service-url" => "URL of the Docker host used for API requests.",
19
+ "shell-command" => "A shell command to wrap any Docker exec commands in, such as `bash -lc`.",
20
+ "tmpdir" => "The directory to upload and execute temporary files on the target.",
21
+ "tty" => "Whether to enable tty on exec commands."
22
+ }.freeze
23
+
10
24
  def self.options
11
- %w[host service-url tmpdir interpreters shell-command tty]
25
+ OPTIONS.keys
12
26
  end
13
27
 
14
28
  def provided_features
@@ -3,8 +3,30 @@
3
3
  module Bolt
4
4
  module Transport
5
5
  class Local < Sudoable
6
+ OPTIONS = {
7
+ "interpreters" => "A map of an extension name to the absolute path of an executable, "\
8
+ "enabling you to override the shebang defined in a task executable. The "\
9
+ "extension can optionally be specified with the `.` character (`.py` and "\
10
+ "`py` both map to a task executable `task.py`) and the extension is case "\
11
+ "sensitive. When a target's name is `localhost`, Ruby tasks run with the "\
12
+ "Bolt Ruby interpreter by default.",
13
+ "run-as" => "A different user to run commands as after login.",
14
+ "run-as-command" => "The command to elevate permissions. Bolt appends the user and command "\
15
+ "strings to the configured `run-as-command` before running it on the target. "\
16
+ "This command must not require an interactive password prompt, and the "\
17
+ "`sudo-password` option is ignored when `run-as-command` is specified. The "\
18
+ "`run-as-command` must be specified as an array.",
19
+ "sudo-executable" => "The executable to use when escalating to the configured `run-as` user. This "\
20
+ "is useful when you want to escalate using the configured `sudo-password`, since "\
21
+ "`run-as-command` does not use `sudo-password` or support prompting. The command "\
22
+ "executed on the target is `<sudo-executable> -S -u <user> -p custom_bolt_prompt "\
23
+ "<command>`. **This option is experimental.**",
24
+ "sudo-password" => "Password to use when changing users via `run-as`.",
25
+ "tmpdir" => "The directory to copy and execute temporary files."
26
+ }.freeze
27
+
6
28
  def self.options
7
- %w[tmpdir interpreters sudo-password run-as run-as-command sudo-executable]
29
+ OPTIONS.keys
8
30
  end
9
31
 
10
32
  def provided_features
@@ -12,8 +12,25 @@ require 'bolt/util'
12
12
  module Bolt
13
13
  module Transport
14
14
  class LocalWindows < Base
15
+ OPTIONS = {
16
+ "interpreters" => "A map of an extension name to the absolute path of an executable, "\
17
+ "enabling you to override the shebang defined in a task executable. The "\
18
+ "extension can optionally be specified with the `.` character (`.py` and "\
19
+ "`py` both map to a task executable `task.py`) and the extension is case "\
20
+ "sensitive. When a target's name is `localhost`, Ruby tasks run with the "\
21
+ "Bolt Ruby interpreter by default.",
22
+ "run-as" => "A different user to run commands as after login.",
23
+ "run-as-command" => "The command to elevate permissions. Bolt appends the user and command "\
24
+ "strings to the configured `run-as-command` before running it on the target. "\
25
+ "This command must not require an interactive password prompt, and the "\
26
+ "`sudo-password` option is ignored when `run-as-command` is specified. The "\
27
+ "`run-as-command` must be specified as an array.",
28
+ "sudo-password" => "Password to use when changing users via `run-as`.",
29
+ "tmpdir" => "The directory to copy and execute temporary files."
30
+ }.freeze
31
+
15
32
  def self.options
16
- %w[tmpdir interpreters run-as run-as-command sudo-password]
33
+ OPTIONS.keys
17
34
  end
18
35
 
19
36
  def provided_features
@@ -21,8 +21,18 @@ module Bolt
21
21
 
22
22
  attr_writer :plan_context
23
23
 
24
+ OPTIONS = {
25
+ "cacert" => "The path to the CA certificate.",
26
+ "host" => "Host name.",
27
+ "job-poll-interval" => "Set interval to poll orchestrator for job status.",
28
+ "job-poll-timeout" => "Set time to wait for orchestrator job status.",
29
+ "service-url" => "The URL of the orchestrator API.",
30
+ "task-environment" => "The environment the orchestrator loads task code from.",
31
+ "token-file" => "The path to the token file."
32
+ }.freeze
33
+
24
34
  def self.options
25
- %w[host service-url cacert token-file task-environment job-poll-interval job-poll-timeout]
35
+ OPTIONS.keys
26
36
  end
27
37
 
28
38
  def self.default_options
@@ -6,6 +6,10 @@ require 'bolt/transport/base'
6
6
  module Bolt
7
7
  module Transport
8
8
  class Remote < Base
9
+ OPTIONS = {
10
+ "run-on" => "The proxy target that the task executes on."
11
+ }.freeze
12
+
9
13
  # The options for the remote transport not defined.
10
14
  def self.filter_options(unfiltered)
11
15
  unfiltered
@@ -8,9 +8,49 @@ require 'shellwords'
8
8
  module Bolt
9
9
  module Transport
10
10
  class SSH < Sudoable
11
+ OPTIONS = {
12
+ "connect-timeout" => "How long to wait when establishing connections.",
13
+ "disconnect-timeout" => "How long to wait before force-closing a connection.",
14
+ "host" => "Host name.",
15
+ "host-key-check" => "Whether to perform host key validation when connecting.",
16
+ "interpreters" => "A map of an extension name to the absolute path of an executable, "\
17
+ "enabling you to override the shebang defined in a task executable. The "\
18
+ "extension can optionally be specified with the `.` character (`.py` and "\
19
+ "`py` both map to a task executable `task.py`) and the extension is case "\
20
+ "sensitive. When a target's name is `localhost`, Ruby tasks run with the "\
21
+ "Bolt Ruby interpreter by default.",
22
+ "password" => "Login password.",
23
+ "port" => "Connection port.",
24
+ "private-key" => "Either the path to the private key file to use for authentication, or a "\
25
+ "hash with the key `key-data` and the contents of the private key.",
26
+ "proxyjump" => "A jump host to proxy connections through, and an optional user to "\
27
+ "connect with.",
28
+ "run-as" => "A different user to run commands as after login.",
29
+ "run-as-command" => "The command to elevate permissions. Bolt appends the user and command "\
30
+ "strings to the configured `run-as-command` before running it on the "\
31
+ "target. This command must not require an interactive password prompt, "\
32
+ "and the `sudo-password` option is ignored when `run-as-command` is "\
33
+ "specified. The `run-as-command` must be specified as an array.",
34
+ "script-dir" => "The subdirectory of the tmpdir to use in place of a randomized "\
35
+ "subdirectory for uploading and executing temporary files on the "\
36
+ "target. It's expected that this directory already exists as a subdir "\
37
+ "of tmpdir, which is either configured or defaults to `/tmp`.",
38
+ "sudo-executable" => "The executable to use when escalating to the configured `run-as` "\
39
+ "user. This is useful when you want to escalate using the configured "\
40
+ "`sudo-password`, since `run-as-command` does not use `sudo-password` "\
41
+ "or support prompting. The command executed on the target is "\
42
+ "`<sudo-executable> -S -u <user> -p custom_bolt_prompt <command>`. "\
43
+ "**This option is experimental.**",
44
+ "sudo-password" => "Password to use when changing users via `run-as`.",
45
+ "tmpdir" => "The directory to upload and execute temporary files on the target.",
46
+ "tty" => "Request a pseudo tty for the session. This option is generally "\
47
+ "only used in conjunction with the `run-as` option when the sudoers "\
48
+ "policy requires a `tty`.",
49
+ "user" => "Login user."
50
+ }.freeze
51
+
11
52
  def self.options
12
- %w[host port user password sudo-password private-key host-key-check sudo-executable
13
- connect-timeout disconnect-timeout tmpdir script-dir run-as tty run-as-command proxyjump interpreters]
53
+ OPTIONS.keys
14
54
  end
15
55
 
16
56
  def self.default_options
@@ -36,7 +36,7 @@ module Bolt
36
36
 
37
37
  @sudo_password = @target.options['sudo-password']
38
38
  # rubocop:disable Style/GlobalVars
39
- @sudo_password ||= @target.options['password'] if $future
39
+ @sudo_password ||= @target.password if $future
40
40
  # rubocop:enable Style/GlobalVars
41
41
 
42
42
  if target.options['private-key']&.instance_of?(String)
@@ -197,13 +197,13 @@ module Bolt
197
197
  if escalate
198
198
  if use_sudo
199
199
  sudo_exec = target.options['sudo-executable'] || "sudo"
200
- sudo_flags = [sudo_exec, "-S", "-u", run_as, "-p", Sudoable.sudo_prompt]
200
+ sudo_flags = [sudo_exec, "-S", "-H", "-u", run_as, "-p", Sudoable.sudo_prompt]
201
201
  sudo_flags += ["-E"] if options[:environment]
202
202
  sudo_str = Shellwords.shelljoin(sudo_flags)
203
203
  else
204
204
  sudo_str = Shellwords.shelljoin(@target.options['run-as-command'] + [run_as])
205
205
  end
206
- command_str = build_sudoable_command_str(command_str, sudo_str, @sudo_id, options)
206
+ command_str = build_sudoable_command_str(command_str, sudo_str, @sudo_id, options.merge(reset_cwd: true))
207
207
  end
208
208
 
209
209
  # Including the environment declarations in the shelljoin will escape
@@ -77,17 +77,24 @@ module Bolt
77
77
  # a random string is echoed to stderr indicating that the stdin is available
78
78
  # for task input data because the sudo password has already either been
79
79
  # provided on stdin or was not needed.
80
- def prepend_sudo_success(sudo_id, command_str)
80
+ def prepend_sudo_success(sudo_id, command_str, reset_cwd)
81
+ command_str = "cd && #{command_str}" if reset_cwd
81
82
  "sh -c 'echo #{sudo_id} 1>&2; #{command_str}'"
82
83
  end
83
84
 
85
+ def prepend_chdir(command_str)
86
+ "sh -c 'cd && #{command_str}'"
87
+ end
88
+
84
89
  # A helper to build up a single string that contains all of the options for
85
90
  # privilege escalation. A wrapper script is used to direct task input to stdin
86
91
  # when a tty is allocated and thus we do not need to prepend_sudo_success when
87
92
  # using the wrapper or when the task does not require stdin data.
88
93
  def build_sudoable_command_str(command_str, sudo_str, sudo_id, options)
89
94
  if options[:stdin] && !options[:wrapper]
90
- "#{sudo_str} #{prepend_sudo_success(sudo_id, command_str)}"
95
+ "#{sudo_str} #{prepend_sudo_success(sudo_id, command_str, options[:reset_cwd])}"
96
+ elsif options[:reset_cwd]
97
+ "#{sudo_str} #{prepend_chdir(command_str)}"
91
98
  else
92
99
  "#{sudo_str} #{command_str}"
93
100
  end
@@ -7,11 +7,35 @@ require 'bolt/transport/powershell'
7
7
  module Bolt
8
8
  module Transport
9
9
  class WinRM < Base
10
+ OPTIONS = {
11
+ "cacert" => "The path to the CA certificate.",
12
+ "connect-timeout" => "How long Bolt should wait when establishing connections.",
13
+ "extensions" => "List of file extensions that are accepted for scripts or tasks. "\
14
+ "Scripts with these file extensions rely on the target's file type "\
15
+ "association to run. For example, if Python is installed on the system, "\
16
+ "a `.py` script runs with `python.exe`. The extensions `.ps1`, `.rb`, and "\
17
+ "`.pp` are always allowed and run via hard-coded executables.",
18
+ "file-protocol" => "Which file transfer protocol to use. Either `winrm` or `smb`. Using `smb` is "\
19
+ "recommended for large file transfers.",
20
+ "host" => "Host name.",
21
+ "interpreters" => "A map of an extension name to the absolute path of an executable, "\
22
+ "enabling you to override the shebang defined in a task executable. The "\
23
+ "extension can optionally be specified with the `.` character (`.py` and "\
24
+ "`py` both map to a task executable `task.py`) and the extension is case "\
25
+ "sensitive. When a target's name is `localhost`, Ruby tasks run with the "\
26
+ "Bolt Ruby interpreter by default.",
27
+ "password" => "Login password. **Required unless using Kerberos.**",
28
+ "port" => "Connection port.",
29
+ "realm" => "Kerberos realm (Active Directory domain) to authenticate against.",
30
+ "smb-port" => "With file-protocol set to smb, this is the port to establish a connection on.",
31
+ "ssl" => "When true, Bolt uses secure https connections for WinRM.",
32
+ "ssl-verify" => "When true, verifies the targets certificate matches the cacert.",
33
+ "tmpdir" => "The directory to upload and execute temporary files on the target.",
34
+ "user" => "Login user. **Required unless using Kerberos.**"
35
+ }.freeze
36
+
10
37
  def self.options
11
- %w[
12
- host port user password connect-timeout ssl ssl-verify tmpdir
13
- cacert extensions interpreters file-protocol smb-port realm
14
- ]
38
+ OPTIONS.keys
15
39
  end
16
40
 
17
41
  def self.default_options
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '1.45.0'
4
+ VERSION = '1.47.0'
5
5
  end
@@ -200,6 +200,19 @@ module BoltServer
200
200
  end
201
201
  end
202
202
 
203
+ def pe_plan_info(pal, module_name, plan_name)
204
+ # Handle case where plan name is simply module name with special `init.pp` plan
205
+ plan_name = if plan_name == 'init' || plan_name.nil?
206
+ module_name
207
+ else
208
+ "#{module_name}::#{plan_name}"
209
+ end
210
+ plan_info = pal.get_plan_info(plan_name)
211
+ # Path to module is meaningless in PE
212
+ plan_info.delete('module')
213
+ plan_info
214
+ end
215
+
203
216
  get '/' do
204
217
  200
205
218
  end
@@ -305,7 +318,7 @@ module BoltServer
305
318
  # @param environment [String] the environment to fetch the plan from
306
319
  get '/plans/:module_name/:plan_name' do
307
320
  in_pe_pal_env(params['environment']) do |pal|
308
- plan_info = pal.get_plan_info("#{params[:module_name]}::#{params[:plan_name]}")
321
+ plan_info = pe_plan_info(pal, params[:module_name], params[:plan_name])
309
322
  [200, plan_info.to_json]
310
323
  end
311
324
  end
@@ -318,7 +331,11 @@ module BoltServer
318
331
  in_pe_pal_env(params['environment']) do |pal|
319
332
  plans = pal.list_plans.flatten
320
333
  if params['metadata']
321
- plan_info = plans.each_with_object({}) { |plan_name, acc| acc[plan_name] = pal.get_plan_info(plan_name) }
334
+ plan_info = plans.each_with_object({}) do |full_name, acc|
335
+ # Break apart module name from plan name
336
+ module_name, plan_name = full_name.split('::', 2)
337
+ acc[full_name] = pe_plan_info(pal, module_name, plan_name)
338
+ end
322
339
  [200, plan_info.to_json]
323
340
  else
324
341
  # We structure this array of plans to be an array of hashes so that it matches the structure
@@ -39,7 +39,8 @@ begin
39
39
 
40
40
  Tempfile.open('plugins.tar.gz') do |plugins|
41
41
  File.binwrite(plugins, Base64.decode64(args['plugins']))
42
- Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, Etc.getlogin || Etc.getpwuid.name)
42
+ user = Etc.getpwuid.nil? ? Etc.getlogin : Etc.getpwuid.name
43
+ Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, user)
43
44
  end
44
45
 
45
46
  env = Puppet.lookup(:environments).get('production')
@@ -21,7 +21,8 @@ Dir.mktmpdir do |puppet_root|
21
21
 
22
22
  Tempfile.open('plugins.tar.gz') do |plugins|
23
23
  File.binwrite(plugins, Base64.decode64(args['plugins']))
24
- Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, Etc.getlogin || Etc.getpwuid.name)
24
+ user = Etc.getpwuid.nil? ? Etc.getlogin : Etc.getpwuid.name
25
+ Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, user)
25
26
  end
26
27
 
27
28
  env = Puppet.lookup(:environments).get('production')
@@ -28,7 +28,8 @@ Dir.mktmpdir do |puppet_root|
28
28
 
29
29
  Tempfile.open('plugins.tar.gz') do |plugins|
30
30
  File.binwrite(plugins, Base64.decode64(args['plugins']))
31
- Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, Etc.getlogin || Etc.getpwuid.name)
31
+ user = Etc.getpwuid.nil? ? Etc.getlogin : Etc.getpwuid.name
32
+ Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, user)
32
33
  end
33
34
 
34
35
  env = Puppet.lookup(:environments).get('production')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.45.0
4
+ version: 1.47.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-13 00:00:00.000000000 Z
11
+ date: 2020-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -420,7 +420,6 @@ files:
420
420
  - lib/bolt/pal/yaml_plan/transpiler.rb
421
421
  - lib/bolt/plan_result.rb
422
422
  - lib/bolt/plugin.rb
423
- - lib/bolt/plugin/install_agent.rb
424
423
  - lib/bolt/plugin/module.rb
425
424
  - lib/bolt/plugin/pkcs7.rb
426
425
  - lib/bolt/plugin/prompt.rb
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bolt
4
- class Plugin
5
- class InstallAgent
6
- def hooks
7
- %i[puppet_library]
8
- end
9
-
10
- def name
11
- 'install_agent'
12
- end
13
-
14
- def initialize(*args); end
15
-
16
- def puppet_library(_opts, target, apply_prep)
17
- install_task = apply_prep.get_task("puppet_agent::install")
18
- service_task = apply_prep.get_task("service", 'action' => 'stop', 'name' => 'puppet')
19
- proc do
20
- apply_prep.run_task([target], install_task).first
21
- apply_prep.set_agent_feature(target)
22
- apply_prep.run_task([target], service_task, 'action' => 'stop', 'name' => 'puppet').first
23
- apply_prep.run_task([target], service_task, 'action' => 'disable', 'name' => 'puppet').first
24
- end
25
- end
26
- end
27
- end
28
- end