bolt 1.27.1 → 1.28.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: 479e933992d2ff527a0c863ed9b4e2d6f94f5726e1c1ef910e3de3e4a92f97d3
4
- data.tar.gz: 19c0c435262e63acb8d94a4adc59bb1613fe126f2e276c66cca392a6074358c0
3
+ metadata.gz: a2d33a034fb69edd25b761aec5e21075aa9ad9f0ccce67122994a2e24dda01f4
4
+ data.tar.gz: '0348c1d2ed663a0c3556795017c219a88c4e432788fe10e8d75bb1f03f0eed89'
5
5
  SHA512:
6
- metadata.gz: a44e8201beb5d720068b0217191495141a34e302748358117d308fd8e6bd4906a05f53e653982a08686d1c2b78c673caec5ddecd9cc5d9b60447a594b299dbc2
7
- data.tar.gz: 42f6ceb89094cf0aac648133b37d477adc8bfdff54c5941a6c8228793d980e4106959e1179f548f3aea296fbc35f27307fdeb874736b5cf3c76fc3ce23b818ae
6
+ metadata.gz: b842c4a35c334729e47f361ea5be9a0467934d1afc718cec50d117f6573f1a2024eac84e4049730c02bd6cc97158dadc7c4ce1d6adff207efbdc103ffc4f0560
7
+ data.tar.gz: 470115a05e6edd058de12934e48e77441105cd83baeadca192d7bf7591b2e5f2bef998164614bee9cbebe0b511fe7507a8dcd61b0fbb19810e75e59bb55c7900
@@ -3,6 +3,10 @@
3
3
  require 'bolt/error'
4
4
 
5
5
  # Parses common ways of referring to targets and returns an array of Targets.
6
+ #
7
+ # **NOTE:** Calling `get_targets` inside an `apply` block with a
8
+ # version 2 inventory creates a new Target object.
9
+ # `get_targets('all')` returns an empty array.
6
10
  Puppet::Functions.create_function(:get_targets) do
7
11
  # @param names A pattern or array of patterns identifying a set of targets.
8
12
  # @return A list of unique Targets resolved from any target URIs and groups.
@@ -29,7 +29,7 @@ module Bolt
29
29
  def self.build_client
30
30
  logger = Logging.logger[self]
31
31
  config_file = File.expand_path('~/.puppetlabs/bolt/analytics.yaml')
32
- config = load_config(config_file)
32
+ config = load_config(config_file, logger)
33
33
 
34
34
  if config['disabled'] || ENV['BOLT_DISABLE_ANALYTICS']
35
35
  logger.debug "Analytics opt-out is set, analytics will be disabled"
@@ -47,10 +47,22 @@ module Bolt
47
47
  NoopClient.new
48
48
  end
49
49
 
50
- def self.load_config(filename)
50
+ def self.load_config(filename, logger)
51
51
  if File.exist?(filename)
52
52
  YAML.load_file(filename)
53
53
  else
54
+ unless ENV['BOLT_DISABLE_ANALYTICS']
55
+ logger.warn <<~ANALYTICS
56
+ Bolt collects data about how you use it. You can opt out of providing this data.
57
+
58
+ To disable analytics data collection, add this line to ~/.puppetlabs/bolt/analytics.yaml :
59
+ disabled: true
60
+
61
+ Read more about what data Bolt collects and why here:
62
+ https://puppet.com/docs/bolt/latest/bolt_installing.html#concept-8242
63
+ ANALYTICS
64
+ end
65
+
54
66
  {}
55
67
  end
56
68
  end
@@ -83,181 +83,181 @@ module Bolt
83
83
  end
84
84
 
85
85
  def self.examples(cmd, desc)
86
- <<-EXAMP
87
- #{desc} a Windows host via WinRM, providing for the password
88
- bolt #{cmd} -n winrm://winhost -u Administrator -p
89
- #{desc} the local machine, a Linux host via SSH, and hosts from a group specified in an inventory file
90
- bolt #{cmd} -n localhost,nixhost,node_group
91
- #{desc} Windows hosts queried from PuppetDB via WinRM as a domain user, prompting for the password
92
- bolt #{cmd} -q 'inventory[certname] { facts.os.family = "windows" }' --transport winrm -u 'domain\\Administrator' -p
93
- EXAMP
86
+ <<~EXAMP
87
+ #{desc} a Windows host via WinRM, providing for the password
88
+ bolt #{cmd} -n winrm://winhost -u Administrator -p
89
+ #{desc} the local machine, a Linux host via SSH, and hosts from a group specified in an inventory file
90
+ bolt #{cmd} -n localhost,nixhost,node_group
91
+ #{desc} Windows hosts queried from PuppetDB via WinRM as a domain user, prompting for the password
92
+ bolt #{cmd} -q 'inventory[certname] { facts.os.family = "windows" }' --transport winrm -u 'domain\\Administrator' -p
93
+ EXAMP
94
94
  end
95
95
 
96
- BANNER = <<-HELP
97
- Usage: bolt <subcommand> <action>
98
-
99
- Available subcommands:
100
- bolt command run <command> Run a command remotely
101
- bolt file upload <src> <dest> Upload a local file or directory
102
- bolt script run <script> Upload a local script and run it remotely
103
- bolt task show Show list of available tasks
104
- bolt task show <task> Show documentation for task
105
- bolt task run <task> [params] Run a Puppet task
106
- bolt plan convert <plan_path> Convert a YAML plan to a Puppet plan
107
- bolt plan show Show list of available plans
108
- bolt plan show <plan> Show details for plan
109
- bolt plan run <plan> [params] Run a Puppet task plan
110
- bolt apply <manifest> Apply Puppet manifest code
111
- bolt puppetfile install Install modules from a Puppetfile into a Boltdir
112
- bolt puppetfile show-modules List modules available to Bolt
113
- bolt secret createkeys Create new encryption keys
114
- bolt secret encrypt <plaintext> Encrypt a value
115
- bolt secret decrypt <encrypted> Decrypt a value
116
- bolt inventory show Show the list of targets an action would run on
117
-
118
- Run `bolt <subcommand> --help` to view specific examples.
119
-
120
- Available options are:
96
+ BANNER = <<~HELP
97
+ Usage: bolt <subcommand> <action>
98
+
99
+ Available subcommands:
100
+ bolt command run <command> Run a command remotely
101
+ bolt file upload <src> <dest> Upload a local file or directory
102
+ bolt script run <script> Upload a local script and run it remotely
103
+ bolt task show Show list of available tasks
104
+ bolt task show <task> Show documentation for task
105
+ bolt task run <task> [params] Run a Puppet task
106
+ bolt plan convert <plan_path> Convert a YAML plan to a Puppet plan
107
+ bolt plan show Show list of available plans
108
+ bolt plan show <plan> Show details for plan
109
+ bolt plan run <plan> [params] Run a Puppet task plan
110
+ bolt apply <manifest> Apply Puppet manifest code
111
+ bolt puppetfile install Install modules from a Puppetfile into a Boltdir
112
+ bolt puppetfile show-modules List modules available to Bolt
113
+ bolt secret createkeys Create new encryption keys
114
+ bolt secret encrypt <plaintext> Encrypt a value
115
+ bolt secret decrypt <encrypted> Decrypt a value
116
+ bolt inventory show Show the list of targets an action would run on
117
+
118
+ Run `bolt <subcommand> --help` to view specific examples.
119
+
120
+ Available options are:
121
121
  HELP
122
122
 
123
- TASK_HELP = <<-HELP
124
- Usage: bolt task <action> <task> [parameters]
123
+ TASK_HELP = <<~HELP
124
+ Usage: bolt task <action> <task> [parameters]
125
125
 
126
- Available actions are:
127
- show Show list of available tasks
128
- show <task> Show documentation for task
129
- run <task> Run a Puppet task
126
+ Available actions are:
127
+ show Show list of available tasks
128
+ show <task> Show documentation for task
129
+ run <task> Run a Puppet task
130
130
 
131
- Parameters are of the form <parameter>=<value>.
131
+ Parameters are of the form <parameter>=<value>.
132
132
 
133
- #{examples('task run facts', 'run facter on')}
134
- Available options are:
133
+ #{examples('task run facts', 'run facter on')}
134
+ Available options are:
135
135
  HELP
136
136
 
137
- TASK_SHOW_HELP = <<-HELP
138
- Usage: bolt task show <task>
137
+ TASK_SHOW_HELP = <<~HELP
138
+ Usage: bolt task show <task>
139
139
 
140
- Available actions are:
141
- show Show list of available tasks
142
- show <task> Show documentation for task
140
+ Available actions are:
141
+ show Show list of available tasks
142
+ show <task> Show documentation for task
143
143
 
144
- Available options are:
144
+ Available options are:
145
145
  HELP
146
146
 
147
- TASK_RUN_HELP = <<-HELP
148
- Usage: bolt task run <task> [parameters]
147
+ TASK_RUN_HELP = <<~HELP
148
+ Usage: bolt task run <task> [parameters]
149
149
 
150
- Parameters are of the form <parameter>=<value>.
150
+ Parameters are of the form <parameter>=<value>.
151
151
 
152
- #{examples('task run facts', 'run facter on')}
153
- Available options are:
152
+ #{examples('task run facts', 'run facter on')}
153
+ Available options are:
154
154
  HELP
155
155
 
156
- COMMAND_HELP = <<-HELP
157
- Usage: bolt command <action> <command>
156
+ COMMAND_HELP = <<~HELP
157
+ Usage: bolt command <action> <command>
158
158
 
159
- Available actions are:
160
- run Run a command remotely
159
+ Available actions are:
160
+ run Run a command remotely
161
161
 
162
- #{examples('command run hostname', 'run hostname on')}
163
- Available options are:
162
+ #{examples('command run hostname', 'run hostname on')}
163
+ Available options are:
164
164
  HELP
165
165
 
166
- SCRIPT_HELP = <<-HELP
167
- Usage: bolt script <action> <script> [[arg1] ... [argN]]
166
+ SCRIPT_HELP = <<~HELP
167
+ Usage: bolt script <action> <script> [[arg1] ... [argN]]
168
168
 
169
- Available actions are:
170
- run Upload a local script and run it remotely
169
+ Available actions are:
170
+ run Upload a local script and run it remotely
171
171
 
172
- #{examples('script run my_script.ps1 some args', 'run a script on')}
173
- Available options are:
172
+ #{examples('script run my_script.ps1 some args', 'run a script on')}
173
+ Available options are:
174
174
  HELP
175
175
 
176
- PLAN_HELP = <<-HELP
177
- Usage: bolt plan <action> <plan> [parameters]
176
+ PLAN_HELP = <<~HELP
177
+ Usage: bolt plan <action> <plan> [parameters]
178
178
 
179
- Available actions are:
180
- convert <plan_path> Convert a YAML plan to a Puppet plan
181
- show Show list of available plans
182
- show <plan> Show details for plan
183
- run Run a Puppet task plan
179
+ Available actions are:
180
+ convert <plan_path> Convert a YAML plan to a Puppet plan
181
+ show Show list of available plans
182
+ show <plan> Show details for plan
183
+ run Run a Puppet task plan
184
184
 
185
- Parameters are of the form <parameter>=<value>.
185
+ Parameters are of the form <parameter>=<value>.
186
186
 
187
- #{examples('plan run canary command=hostname', 'run the canary plan on')}
188
- Available options are:
187
+ #{examples('plan run canary command=hostname', 'run the canary plan on')}
188
+ Available options are:
189
189
  HELP
190
190
 
191
- PLAN_CONVERT_HELP = <<-HELP
192
- Usage: bolt plan convert <plan_path>
191
+ PLAN_CONVERT_HELP = <<~HELP
192
+ Usage: bolt plan convert <plan_path>
193
193
 
194
- Available options are:
194
+ Available options are:
195
195
  HELP
196
196
 
197
- PLAN_SHOW_HELP = <<-HELP
198
- Usage: bolt plan show <plan>
197
+ PLAN_SHOW_HELP = <<~HELP
198
+ Usage: bolt plan show <plan>
199
199
 
200
- Available actions are:
201
- show Show list of available plans
202
- show <plan> Show details for plan
200
+ Available actions are:
201
+ show Show list of available plans
202
+ show <plan> Show details for plan
203
203
 
204
- Available options are:
204
+ Available options are:
205
205
  HELP
206
206
 
207
- PLAN_RUN_HELP = <<-HELP
208
- Usage: bolt plan run <plan> [parameters]
207
+ PLAN_RUN_HELP = <<~HELP
208
+ Usage: bolt plan run <plan> [parameters]
209
209
 
210
- Parameters are of the form <parameter>=<value>.
210
+ Parameters are of the form <parameter>=<value>.
211
211
 
212
- #{examples('plan run canary command=hostname', 'run the canary plan on')}
213
- Available options are:
212
+ #{examples('plan run canary command=hostname', 'run the canary plan on')}
213
+ Available options are:
214
214
  HELP
215
215
 
216
- FILE_HELP = <<-HELP
217
- Usage: bolt file <action>
216
+ FILE_HELP = <<~HELP
217
+ Usage: bolt file <action>
218
218
 
219
- Available actions are:
220
- upload <src> <dest> Upload local file or directory <src> to <dest> on each node
219
+ Available actions are:
220
+ upload <src> <dest> Upload local file or directory <src> to <dest> on each node
221
221
 
222
- #{examples('file upload /tmp/source /etc/profile.d/login.sh', 'upload a file to')}
223
- Available options are:
222
+ #{examples('file upload /tmp/source /etc/profile.d/login.sh', 'upload a file to')}
223
+ Available options are:
224
224
  HELP
225
225
 
226
- PUPPETFILE_HELP = <<-HELP
227
- Usage: bolt puppetfile <action>
226
+ PUPPETFILE_HELP = <<~HELP
227
+ Usage: bolt puppetfile <action>
228
228
 
229
- Available actions are:
230
- install Install modules from a Puppetfile into a Boltdir
231
- show-modules List modules available to Bolt
229
+ Available actions are:
230
+ install Install modules from a Puppetfile into a Boltdir
231
+ show-modules List modules available to Bolt
232
232
 
233
- Install modules into the local Boltdir
234
- bolt puppetfile install
233
+ Install modules into the local Boltdir
234
+ bolt puppetfile install
235
235
 
236
- Available options are:
236
+ Available options are:
237
237
  HELP
238
238
 
239
- PUPPETFILE_INSTALL_HELP = <<-HELP
240
- Usage: bolt puppetfile install
239
+ PUPPETFILE_INSTALL_HELP = <<~HELP
240
+ Usage: bolt puppetfile install
241
241
 
242
- Install modules into the local Boltdir
243
- bolt puppetfile install
242
+ Install modules into the local Boltdir
243
+ bolt puppetfile install
244
244
 
245
- Available options are:
245
+ Available options are:
246
246
  HELP
247
247
 
248
- PUPPETFILE_SHOWMODULES_HELP = <<-HELP
249
- Usage: bolt puppetfile show-modules
248
+ PUPPETFILE_SHOWMODULES_HELP = <<~HELP
249
+ Usage: bolt puppetfile show-modules
250
250
 
251
- Available options are:
251
+ Available options are:
252
252
  HELP
253
253
 
254
- APPLY_HELP = <<-HELP
255
- Usage: bolt apply <manifest.pp>
254
+ APPLY_HELP = <<~HELP
255
+ Usage: bolt apply <manifest.pp>
256
256
 
257
- #{examples('apply site.pp', 'apply a manifest on')}
258
- bolt apply site.pp --nodes foo.example.com,bar.example.com
257
+ #{examples('apply site.pp', 'apply a manifest on')}
258
+ bolt apply site.pp --nodes foo.example.com,bar.example.com
259
259
 
260
- Available options are:
260
+ Available options are:
261
261
  HELP
262
262
 
263
263
  SECRET_HELP = <<~SECRET_HELP
@@ -31,13 +31,13 @@ module Bolt
31
31
  end
32
32
 
33
33
  class Config
34
- attr_accessor :aws, :concurrency, :format, :trace, :log, :puppetdb, :color, :save_rerun,
34
+ attr_accessor :concurrency, :format, :trace, :log, :puppetdb, :color, :save_rerun,
35
35
  :transport, :transports, :inventoryfile, :compile_concurrency, :boltdir,
36
36
  :puppetfile_config, :plugins
37
37
  attr_writer :modulepath
38
38
 
39
39
  TRANSPORT_OPTIONS = %i[password run-as sudo-password extensions
40
- private-key tty tmpdir user connect-timeout
40
+ private-key tty tmpdir user connect-timeout disconnect-timeout
41
41
  cacert token-file service-url interpreters file-protocol smb-port realm].freeze
42
42
 
43
43
  PUPPETFILE_OPTIONS = %w[proxy forge].freeze
@@ -162,7 +162,7 @@ module Bolt
162
162
  # Plugins are only settable from config not inventory so we can overwrite
163
163
  @plugins = data['plugins'] if data.key?('plugins')
164
164
 
165
- %w[aws concurrency format puppetdb color transport].each do |key|
165
+ %w[concurrency format puppetdb color transport].each do |key|
166
166
  send("#{key}=", data[key]) if data.key?(key)
167
167
  end
168
168
 
@@ -88,6 +88,8 @@ module Bolt
88
88
  end
89
89
 
90
90
  def resources_step(scope, step)
91
+ # TODO: Only call apply_prep when needed
92
+ scope.call_function('apply_prep', step['target'])
91
93
  manifest = generate_manifest(step['resources'])
92
94
 
93
95
  apply_manifest(scope, step['target'], manifest)
@@ -65,6 +65,13 @@ module Bolt
65
65
 
66
66
  def transpile
67
67
  code = StringIO.new
68
+
69
+ code.print " "
70
+ fn = 'apply_prep'
71
+ args = [@target]
72
+ code << function_call(fn, args)
73
+ code.print "\n"
74
+
68
75
  code.print " "
69
76
  code.print "$#{@name} = " if @name
70
77
 
@@ -6,6 +6,7 @@ require 'bolt/plugin/pkcs7'
6
6
  require 'bolt/plugin/prompt'
7
7
  require 'bolt/plugin/task'
8
8
  require 'bolt/plugin/aws'
9
+ require 'bolt/plugin/vault'
9
10
 
10
11
  module Bolt
11
12
  class Plugin
@@ -37,7 +38,8 @@ module Bolt
37
38
  plugins.add_plugin(Bolt::Plugin::Prompt.new)
38
39
  plugins.add_plugin(Bolt::Plugin::Pkcs7.new(config.boltdir.path, config.plugins['pkcs7'] || {}))
39
40
  plugins.add_plugin(Bolt::Plugin::Task.new(config))
40
- plugins.add_plugin(Bolt::Plugin::Aws::EC2.new(config))
41
+ plugins.add_plugin(Bolt::Plugin::Aws::EC2.new(config.plugins['aws'] || {}))
42
+ plugins.add_plugin(Bolt::Plugin::Vault.new(config.plugins['vault'] || {}))
41
43
  plugins
42
44
  end
43
45
 
@@ -34,12 +34,12 @@ module Bolt
34
34
  if opts.key?('profile')
35
35
  options[:profile] = opts['profile']
36
36
  end
37
- if config.aws&.key?('credentials')
38
- creds = File.expand_path(config.aws['credentials'])
37
+ if config['credentials']
38
+ creds = File.expand_path(config['credentials'])
39
39
  if File.exist?(creds)
40
40
  options[:credentials] = ::Aws::SharedCredentials.new(path: creds)
41
41
  else
42
- raise Bolt::ValidationError, "Cannot load credentials file #{config.aws['credentials']}"
42
+ raise Bolt::ValidationError, "Cannot load credentials file #{config['credentials']}"
43
43
  end
44
44
  end
45
45
 
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Plugin
5
+ class Vault
6
+ class VaultHTTPError < Bolt::Error
7
+ def initialize(response)
8
+ err = JSON.parse(response.body)['errors']
9
+ m = String.new("#{response.code} \"#{response.msg}\"")
10
+ m << ": #{err.join(';')}" unless err.nil?
11
+ super(m, 'bolt.plugin/vault-http-error')
12
+ end
13
+ end
14
+
15
+ attr_reader :config
16
+
17
+ # All requests for secrets must have a token in the request header
18
+ TOKEN_HEADER = "X-Vault-Token"
19
+
20
+ # Default header for all requests, including auth methods
21
+ DEFAULT_HEADER = {
22
+ "Content-Type" => "application/json",
23
+ "Accept" => "application/json"
24
+ }.freeze
25
+
26
+ # Make sure no unexpected keys are in the config
27
+ def validate_config(config)
28
+ known_keys = %w[server_url auth cacert]
29
+
30
+ config.each do |key, _v|
31
+ next if known_keys.include?(key)
32
+ raise Bolt::ValidationError, "Unexpected key in Vault plugin config: #{key}"
33
+ end
34
+ end
35
+
36
+ # Make sure no unexpected keys are in the inventory config and
37
+ # that required keys are present
38
+ def validate_options(opts)
39
+ known_keys = %w[_plugin server_url auth path field version cacert]
40
+ required_keys = %w[path]
41
+
42
+ opts.each do |key, _v|
43
+ next if known_keys.include?(key)
44
+ raise Bolt::ValidationError, "Unexpected key in inventory config: #{key}"
45
+ end
46
+
47
+ required_keys.each do |key|
48
+ next if opts[key]
49
+ raise Bolt::ValidationError, "Expected key in inventory config: #{key}"
50
+ end
51
+ end
52
+
53
+ def name
54
+ 'vault'
55
+ end
56
+
57
+ def hooks
58
+ ['inventory_config']
59
+ end
60
+
61
+ def initialize(config)
62
+ validate_config(config)
63
+ @config = config
64
+ @logger = Logging.logger[self]
65
+ end
66
+
67
+ def inventory_config(opts)
68
+ validate_options(opts)
69
+
70
+ header = {
71
+ TOKEN_HEADER => token(opts)
72
+ }
73
+
74
+ response = request(:Get, uri(opts), opts, nil, header)
75
+
76
+ parse_response(response, opts)
77
+ end
78
+
79
+ # Request uri - built up from Vault server url and secrets path
80
+ def uri(opts, path = nil)
81
+ url = opts['server_url'] || config['server_url'] || ENV['VAULT_ADDR']
82
+
83
+ # Handle the different versions of the API
84
+ if opts['version'] == 2
85
+ mount, store = opts['path'].split('/', 2)
86
+ opts['path'] = [mount, 'data', store].join('/')
87
+ end
88
+
89
+ path ||= opts['path']
90
+
91
+ URI.parse(File.join(url, "v1", path))
92
+ end
93
+
94
+ # Configure the http/s client
95
+ def client(uri, opts)
96
+ client = Net::HTTP.new(uri.host, uri.port)
97
+
98
+ if uri.scheme == 'https'
99
+ cacert = opts['cacert'] || config['cacert'] || ENV['VAULT_CACERT']
100
+
101
+ unless cacert
102
+ raise Bolt::ValidationError, "Expected cacert to be set when using https"
103
+ end
104
+
105
+ client.use_ssl = true
106
+ client.ssl_version = :TLSv1_2
107
+ client.ca_file = cacert
108
+ client.verify_mode = OpenSSL::SSL::VERIFY_PEER
109
+ end
110
+
111
+ client
112
+ end
113
+
114
+ # Auth token to vault server
115
+ def token(opts)
116
+ if (auth = opts['auth'] || config['auth'])
117
+ request_token(auth, opts)
118
+ else
119
+ ENV['VAULT_TOKEN']
120
+ end
121
+ end
122
+
123
+ def request(verb, uri, opts, data, header = {})
124
+ # Add on any header options
125
+ header = DEFAULT_HEADER.merge(header)
126
+
127
+ # Create the HTTP request
128
+ client = client(uri, opts)
129
+ request = Net::HTTP.const_get(verb).new(uri.request_uri, header)
130
+
131
+ # Attach any data
132
+ request.body = data if data
133
+
134
+ # Send the request
135
+ begin
136
+ response = client.request(request)
137
+ rescue StandardError => e
138
+ raise Bolt::Error.new(
139
+ "Failed to connect to #{uri}: #{e.message}",
140
+ 'CONNECT_ERROR'
141
+ )
142
+ end
143
+
144
+ case response
145
+ when Net::HTTPOK
146
+ JSON.parse(response.body)
147
+ else
148
+ raise VaultHTTPError, response
149
+ end
150
+ end
151
+
152
+ def parse_response(response, opts)
153
+ data = case opts['version']
154
+ when 2
155
+ response['data']['data']
156
+ else
157
+ response['data']
158
+ end
159
+
160
+ if opts['field']
161
+ unless data[opts['field']]
162
+ raise Bolt::ValidationError, "Unknown secrets field: #{opts['field']}"
163
+ end
164
+ data[opts['field']]
165
+ else
166
+ data
167
+ end
168
+ end
169
+
170
+ # Request a token from Vault using one of the auth methods
171
+ def request_token(auth, opts)
172
+ case auth['method']
173
+ when 'token'
174
+ auth_token(auth)
175
+ when 'userpass'
176
+ auth_userpass(auth, opts)
177
+ else
178
+ raise Bolt::ValidationError, "Unknown auth method: #{auth['method']}"
179
+ end
180
+ end
181
+
182
+ def validate_auth(auth, required_keys)
183
+ required_keys.each do |key|
184
+ next if auth[key]
185
+ raise Bolt::ValidationError, "Expected key in #{auth['method']} auth method: #{key}"
186
+ end
187
+ end
188
+
189
+ # Authenticate with Vault using the 'Token' auth method
190
+ def auth_token(auth)
191
+ validate_auth(auth, %w[token])
192
+ auth['token']
193
+ end
194
+
195
+ # Authenticate with Vault using the 'Userpass' auth method
196
+ def auth_userpass(auth, opts)
197
+ validate_auth(auth, %w[user pass])
198
+ path = "auth/userpass/login/#{auth['user']}"
199
+ uri = uri(opts, path)
200
+ data = { "password" => auth['pass'] }.to_json
201
+
202
+ request(:Post, uri, opts, data)['auth']['client_token']
203
+ end
204
+ end
205
+ end
206
+ end
@@ -10,7 +10,7 @@ module Bolt
10
10
  class SSH < Sudoable
11
11
  def self.options
12
12
  %w[host port user password sudo-password private-key host-key-check
13
- connect-timeout tmpdir run-as tty run-as-command proxyjump interpreters]
13
+ connect-timeout disconnect-timeout tmpdir run-as tty run-as-command proxyjump interpreters]
14
14
  end
15
15
 
16
16
  def self.default_options
@@ -18,7 +18,8 @@ module Bolt
18
18
  'connect-timeout' => 10,
19
19
  'host-key-check' => true,
20
20
  'tty' => false,
21
- 'load-config' => true
21
+ 'load-config' => true,
22
+ 'disconnect-timeout' => 5
22
23
  }
23
24
  end
24
25
 
@@ -42,10 +43,12 @@ module Bolt
42
43
  end
43
44
  end
44
45
 
45
- timeout_value = options['connect-timeout']
46
- unless timeout_value.is_a?(Integer) || timeout_value.nil?
47
- error_msg = "connect-timeout value must be an Integer, received #{timeout_value}:#{timeout_value.class}"
48
- raise Bolt::ValidationError, error_msg
46
+ %w[connect-timeout disconnect-timeout].each do |timeout|
47
+ timeout_value = options[timeout]
48
+ unless timeout_value.is_a?(Integer) || timeout_value.nil?
49
+ error_msg = "#{timeout} value must be an Integer, received #{timeout_value}:#{timeout_value.class}"
50
+ raise Bolt::ValidationError, error_msg
51
+ end
49
52
  end
50
53
  end
51
54
 
@@ -130,7 +130,11 @@ module Bolt
130
130
 
131
131
  def disconnect
132
132
  if @session && !@session.closed?
133
- @session.close
133
+ begin
134
+ Timeout.timeout(@target.options['disconnect-timeout']) { @session.close }
135
+ rescue Timeout::Error
136
+ @session.shutdown!
137
+ end
134
138
  @logger.debug { "Closed session" }
135
139
  end
136
140
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '1.27.1'
4
+ VERSION = '1.28.0'
5
5
  end
@@ -28,6 +28,10 @@
28
28
  "type": "integer",
29
29
  "description": "How long Bolt should wait when establishing connections"
30
30
  },
31
+ "disconnect-timeout": {
32
+ "type": "integer",
33
+ "description": "How long Bolt should wait before forcing a disconnect"
34
+ },
31
35
  "run-as-command": {
32
36
  "type": "array",
33
37
  "description": "Command elevate permissions",
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.27.1
4
+ version: 1.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-07-31 00:00:00.000000000 Z
11
+ date: 2019-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -416,6 +416,7 @@ files:
416
416
  - lib/bolt/plugin/puppetdb.rb
417
417
  - lib/bolt/plugin/task.rb
418
418
  - lib/bolt/plugin/terraform.rb
419
+ - lib/bolt/plugin/vault.rb
419
420
  - lib/bolt/puppetdb.rb
420
421
  - lib/bolt/puppetdb/client.rb
421
422
  - lib/bolt/puppetdb/config.rb