train-winrm 0.2.6 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f72fe0398deeb2ddebaa5bce7177f0bfb5e85394c371a5443d8d08f2dec8c009
4
- data.tar.gz: f2201788d2a72840c262312953b57f4d9c391b22ed435c66fa81774539469b3b
3
+ metadata.gz: e0ec8bfbda71e6a294ed4ca66e9e86ca9fe26c52f766b26a14a42435119d9d3e
4
+ data.tar.gz: cfaeaa2c5294859943d47691d0b43754fec79c6ab34a33c6fd9b9a8662cb3740
5
5
  SHA512:
6
- metadata.gz: 411d8547046c5a3c85bcf327a806227604df7dfa5cdb3ee1972bb6ae5456b96e949e28497f0b0f5af68d402b1802b39282b3271a2c9646908d46e868bf21bff0
7
- data.tar.gz: 73045b37df8ed789d94e1aa476b2370128ac812f945c2a9819cb6e15acf977d8483ba241b7dec498b5756482ba080df79f6223ac3c70c005fcfb6b458419186f
6
+ metadata.gz: 55e4978c8bcbc83858317ddcd4eaa467eaa37a19219b0e4e4a8f8875021bebbcccc68eabb3197634277dc66a23feb3f53024008ec9409141988ec7552686e193
7
+ data.tar.gz: a677486b1dcfb590028654948bc9ec9684d718586116caa7f5086028f5feccf747cb6da470ea1e6240ae50eaf31b12a137a01a6cc6e26209631538d6ca49afe6
@@ -11,7 +11,7 @@
11
11
  libdir = File.dirname(__FILE__)
12
12
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
13
13
 
14
- # It's traditonal to keep your gem version in a separate file, so CI can find it easier.
14
+ # It's traditional to keep your gem version in a separate file, so CI can find it easier.
15
15
  require_relative "train-winrm/version"
16
16
 
17
17
  # A train plugin has three components: Transport, Connection, and (optionally) Platform.
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  #
4
2
  # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
5
3
  # Author:: Matt Wrock (<matt@mattwrock.com>)
@@ -35,6 +33,8 @@
35
33
 
36
34
  require "train"
37
35
  require "train/plugins"
36
+ # This module may need to directly require WinRM to reference its exception classes
37
+ require "winrm" unless defined?(WinRM)
38
38
 
39
39
  module TrainPlugins
40
40
  module WinRM
@@ -49,6 +49,7 @@ module TrainPlugins
49
49
  @connection_retry_sleep = @options.delete(:connection_retry_sleep)
50
50
  @max_wait_until_ready = @options.delete(:max_wait_until_ready)
51
51
  @operation_timeout = @options.delete(:operation_timeout)
52
+ @shell_type = @options.delete(:winrm_shell_type)
52
53
  end
53
54
 
54
55
  # (see Base::Connection#close)
@@ -109,15 +110,47 @@ module TrainPlugins
109
110
  Train::File::Remote::Windows.new(self, path)
110
111
  end
111
112
 
112
- def run_command_via_connection(command, &data_handler)
113
+ def run_command_via_connection(command, opts = {}, &data_handler)
113
114
  return if command.nil?
114
115
 
115
116
  logger.debug("[WinRM] #{self} (#{command})")
116
117
  out = ""
118
+ response = nil
119
+ timeout = opts[:timeout]&.to_i
120
+
121
+ # Run the command in a thread, to support timing out the command
122
+ thr = Thread.new do
123
+ # Surface any exceptions in this thread back to this method
124
+ Thread.current.report_on_exception = false
125
+ Thread.current.abort_on_exception = true
126
+ begin
127
+ response = session.run(command) do |stdout, _|
128
+ yield(stdout) if data_handler && stdout
129
+ out << stdout if stdout
130
+ end
131
+ rescue ::WinRM::WinRMHTTPTransportError => e
132
+ # If this command hits timeout, there is also a potential race in the HTTP transport
133
+ # where decryption is attempted on an empty message.
134
+ raise e unless timeout && e.to_s == "Could not decrypt NTLM message. ()."
135
+ rescue RuntimeError => e
136
+ # Ref: https://github.com/WinRb/WinRM/issues/315
137
+ # If this command hits timeout, calling close with the command currently running causes
138
+ # a RuntimeError error in WinRM's cleanup code. This specific error can be ignored.
139
+ # The command will be terminated and further commands can be sent on the connection.
140
+ raise e unless timeout && e.to_s == "opts[:shell_id] is required"
141
+ end
142
+ end
117
143
 
118
- response = session.run(command) do |stdout, _|
119
- yield(stdout) if data_handler && stdout
120
- out << stdout if stdout
144
+ if timeout
145
+ res = thr.join(timeout)
146
+ unless res
147
+ msg = "PowerShell command '(#{command})' reached timeout (#{timeout}s)"
148
+ logger.info("[WinRM] #{msg}")
149
+ close
150
+ raise Train::CommandTimeoutReached.new msg
151
+ end
152
+ else
153
+ thr.join
121
154
  end
122
155
 
123
156
  CommandResult.new(out, response.stderr, response.exitcode)
@@ -197,7 +230,7 @@ module TrainPlugins
197
230
  opts[:operation_timeout] = @operation_timeout unless @operation_timeout.nil?
198
231
  @service = ::WinRM::Connection.new(options.merge(opts))
199
232
  @service.logger = logger
200
- @service.shell(:powershell)
233
+ @service.shell(@shell_type)
201
234
  end
202
235
  end
203
236
 
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  #
4
2
  # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
5
3
  # Author:: Matt Wrock (<matt@mattwrock.com>)
@@ -21,8 +19,6 @@
21
19
  # See the License for the specific language governing permissions and
22
20
  # limitations under the License.
23
21
 
24
- require "rbconfig"
25
- require "uri"
26
22
  require "train"
27
23
  require "train/errors"
28
24
  require "train/plugins"
@@ -50,6 +46,7 @@ module TrainPlugins
50
46
 
51
47
  # ref: https://github.com/winrb/winrm#transports
52
48
  SUPPORTED_WINRM_TRANSPORTS = %i{negotiate ssl plaintext kerberos}.freeze
49
+ SUPPORTED_WINRM_SHELL_TYPES = %i{powershell elevated cmd}.freeze
53
50
 
54
51
  # common target configuration
55
52
  option :host, required: true
@@ -76,6 +73,7 @@ module TrainPlugins
76
73
  # from the winrm endpoint. Does not mean that the command has
77
74
  # completed in this time, only that the server has ack'd the request.
78
75
  option :operation_timeout, default: nil
76
+ option :winrm_shell_type , default: "powershell"
79
77
 
80
78
  def initialize(opts)
81
79
  super(opts)
@@ -109,6 +107,11 @@ module TrainPlugins
109
107
  raise Train::ClientError, "Unsupported transport type: #{winrm_transport.inspect}"
110
108
  end
111
109
 
110
+ winrm_shell_type = opts[:winrm_shell_type].to_sym
111
+ unless SUPPORTED_WINRM_SHELL_TYPES.include?(winrm_shell_type)
112
+ raise Train::ClientError, "Unsupported winrm shell type: #{winrm_shell_type.inspect}"
113
+ end
114
+
112
115
  # remove leading '/'
113
116
  path = (opts[:path] || "").sub(%r{^/+}, "")
114
117
 
@@ -116,6 +119,7 @@ module TrainPlugins
116
119
  end
117
120
 
118
121
  WINRM_FS_SPEC_VERSION = "~> 1.0".freeze
122
+ WINRM_ELEVATED_SPEC_VERSION = "~> 1.2.2".freeze
119
123
 
120
124
  # Builds the hash of options needed by the Connection object on
121
125
  # construction.
@@ -142,13 +146,14 @@ module TrainPlugins
142
146
  service: opts[:kerberos_service],
143
147
  ca_trust_file: opts[:ca_trust_file],
144
148
  ssl_peer_fingerprint: opts[:ssl_peer_fingerprint],
149
+ winrm_shell_type: opts[:winrm_shell_type],
145
150
  }
146
151
  end
147
152
 
148
153
  # Creates a new WinRM Connection instance and save it for potential
149
154
  # future reuse.
150
155
  #
151
- # @param options [Hash] conneciton options
156
+ # @param options [Hash] connection options
152
157
  # @return [WinRM::Connection] a WinRM Connection instance
153
158
  # @api private
154
159
  def create_new_connection(options, &block)
@@ -166,32 +171,41 @@ module TrainPlugins
166
171
  spec_version = WINRM_FS_SPEC_VERSION.dup
167
172
  logger.debug("winrm-fs requested," \
168
173
  " loading WinRM::FS gem (#{spec_version})")
169
- gem "winrm-fs", spec_version
170
- first_load = require "winrm-fs"
171
- load_winrm_transport!
174
+ load_dependency("winrm-fs", spec_version)
175
+
176
+ spec_version = WINRM_ELEVATED_SPEC_VERSION.dup
177
+ logger.debug("winrm-elevated requested," \
178
+ " loading WinRM-elevated gem (#{spec_version})")
179
+ load_dependency("winrm-elevated", spec_version)
180
+ end
181
+
182
+ def load_dependency(gem_name, spec_version)
183
+ gem gem_name, spec_version
184
+ first_load = require gem_name
185
+ load_winrm_transport!(gem_name)
172
186
 
173
187
  if first_load
174
- logger.debug("WinRM::FS library loaded")
188
+ logger.debug("#{gem_name} library loaded")
175
189
  else
176
- logger.debug("WinRM::FS previously loaded")
190
+ logger.debug("#{gem_name} previously loaded")
177
191
  end
178
192
  rescue LoadError => e
179
193
  logger.fatal(
180
- "The `winrm-fs' gem is missing and must" \
194
+ "The `#{gem_name}' gem is missing and must" \
181
195
  " be installed or cannot be properly activated. Run" \
182
- " `gem install winrm-fs --version '#{spec_version}'`" \
196
+ " `gem install #{gem_name} --version '#{spec_version}'`" \
183
197
  " or add the following to your Gemfile if you are using Bundler:" \
184
- " `gem 'winrm-fs', '#{spec_version}'`."
198
+ " `gem '#{gem_name}', '#{spec_version}'`."
185
199
  )
186
200
  raise Train::UserError,
187
- "Could not load or activate WinRM::FS (#{e.message})"
201
+ "Could not load or activate #{gem_name} (#{e.message})"
188
202
  end
189
203
 
190
204
  # Load WinRM::Transport code.
191
205
  #
192
206
  # @api private
193
- def load_winrm_transport!
194
- silence_warnings { require "winrm-fs" }
207
+ def load_winrm_transport!(gem_name)
208
+ silence_warnings { require gem_name }
195
209
  end
196
210
 
197
211
  # Return the last saved WinRM connection instance.
@@ -5,6 +5,6 @@
5
5
 
6
6
  module TrainPlugins
7
7
  module WinRM
8
- VERSION = "0.2.6".freeze
8
+ VERSION = "0.2.11".freeze
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: train-winrm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef InSpec Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-31 00:00:00.000000000 Z
11
+ date: 2020-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: winrm
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: winrm-elevated
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.2
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: winrm-fs
29
43
  requirement: !ruby/object:Gem::Requirement