winrm-transport 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8fceb01bb304b3dbde260f959336ea7dc319dd81
4
+ data.tar.gz: 35dfc04b0d86e4ec094e6646b2e0ffc34224e623
5
+ SHA512:
6
+ metadata.gz: 5ea5f23b2e9bc90cd2fe5c8db87b18fed3b5061e9b4858160cef498583b07c0ca7fc47da1a22f33e244b17a17bef653233070456fc953afa1f27bcfac1b2a0d0
7
+ data.tar.gz: d2ac24211bd20bc50cfaae3c5c02c430293bf5701975072fb50cb429948db816551aa9a486ee136b69193c475f17665e9455ee30eaf566c22e078a012ff4be4b
data/.cane ADDED
@@ -0,0 +1,2 @@
1
+ --abc-max 20
2
+ --style-measure 100
@@ -0,0 +1,15 @@
1
+ *.gem
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ /.rvmrc
12
+ /.rbenv-version
13
+ /.ruby-version
14
+ /.project
15
+ /.DS_Store
@@ -0,0 +1,26 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2
5
+ - 2.1
6
+ - 2.0.0
7
+ - 1.9.3
8
+ - ruby-head
9
+
10
+ before_install:
11
+ - echo "Updating Bundler to ~> 1.9 until TravisCI has upgraded."
12
+ - gem install bundler -v "~> 1.9"
13
+ - bundle --version
14
+
15
+ bundler_args: --without guard
16
+
17
+ sudo: false
18
+
19
+ matrix:
20
+ allow_failures:
21
+ - rvm: ruby-head
22
+
23
+ addons:
24
+ code_climate:
25
+ repo_token:
26
+ secure: "WSrIaJVtAh88T7cd3DQY38yxiDz44Oms4Z5Dm+mVUv8gpKWixMR3t5ShmDW8+XXWWj3s3WUa2t5yZugrfz0gkrKCxSII6VURakBKy2jEKnEnJWoh8LKYjXHYW2fOU/PpclSFl0Ynxvt8Nn/F9dpbaGAqj2DwycAV4EtByuy06X0="
@@ -0,0 +1,3 @@
1
+ ## 1.0.0 / 2015-03-29
2
+
3
+ The initial release.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # -*- encoding: utf-8 -*-
2
+ source "https://rubygems.org"
3
+ gemspec
4
+
5
+ group :guard do
6
+ gem "guard-minitest"
7
+ gem "guard-rubocop"
8
+ gem "guard-yard"
9
+ end
10
+
11
+ group :test do
12
+ gem "codeclimate-test-reporter", :require => nil
13
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ ignore %r{^\.gem/}
3
+
4
+ def rubocop_opts
5
+ { :all_on_start => false, :keep_failed => false, :cli => "-r finstyle" }
6
+ end
7
+
8
+ def yard_opts
9
+ { :port => "8808" }
10
+ end
11
+
12
+ group :red_green_refactor, :halt_on_fail => true do
13
+ guard :minitest do
14
+ watch(%r{^spec/(.*)_spec\.rb})
15
+ watch(%r{^lib/(.*)([^/]+)\.rb}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
16
+ watch(%r{^spec/spec_helper\.rb}) { "spec" }
17
+ end
18
+
19
+ guard :rubocop, rubocop_opts do
20
+ watch(%r{.+\.rb$})
21
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
22
+ end
23
+ end
24
+
25
+ guard :yard, yard_opts do
26
+ watch(%r{lib/.+\.rb})
27
+ end
@@ -0,0 +1,15 @@
1
+ Author:: Fletcher Nichol (<fnichol@nichol.ca>)
2
+
3
+ Copyright 2015 Fletcher Nichol
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
@@ -0,0 +1,80 @@
1
+ # WinRM::Transport
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/winrm-transport.svg)](http://badge.fury.io/rb/winrm-transport)
4
+ [![Build Status](https://secure.travis-ci.org/test-kitchen/winrm-transport.svg?branch=master)](https://travis-ci.org/test-kitchen/winrm-transport)
5
+ [![Code Climate](https://codeclimate.com/github/test-kitchen/winrm-transport.svg)](https://codeclimate.com/github/test-kitchen/winrm-transport)
6
+ [![Test Coverage](https://codeclimate.com/github/test-kitchen/winrm-transport/badges/coverage.svg)](https://codeclimate.com/github/test-kitchen/winrm-transport)
7
+ [![Inline docs](http://inch-ci.org/github/test-kitchen/winrm-transport.svg?branch=master)](http://inch-ci.org/github/test-kitchen/winrm-transport)
8
+ [![Dependency Status](https://gemnasium.com/test-kitchen/winrm-transport.svg)](https://gemnasium.com/test-kitchen/winrm-transport)
9
+
10
+ WinRM transport logic for re-using remote shells and uploading files. The original code was extracted from the [Test Kitchen][test_kitchen] project and remains the primary reference use case.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'winrm-transport'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install winrm-transport
27
+
28
+ ## Usage
29
+
30
+ This a library gem and doesn't have any CLI commands. There are 2 primary object classes:
31
+
32
+ * [WinRM::Transport::CommandExecutor][command_executor]: an object which can
33
+ execute multiple commands and PowerShell script in one shared remote shell
34
+ session.
35
+ * [WinRM::Transport::FileTransporter][file_transporter]: an object which can
36
+ upload one or more files or directories to a remote host over WinRM only
37
+ using PowerShell scripts and CMD commands.
38
+
39
+ ## Versioning
40
+
41
+ WinRM::Transport aims to adhere to [Semantic Versioning 2.0.0][semver].
42
+
43
+ ## Development
44
+
45
+ * Source hosted at [GitHub][repo]
46
+ * Report issues/questions/feature requests on [GitHub Issues][issues]
47
+
48
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
49
+ `bin/console` for an interactive prompt that will allow you to experiment.
50
+
51
+ ## Contributing
52
+
53
+ Pull requests are very welcome! Make sure your patches are well tested.
54
+ Ideally create a topic branch for every separate change you make. For
55
+ example:
56
+
57
+ 1. Fork it ( https://github.com/test-kitchen/winrm-transport/fork )
58
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
59
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
60
+ 4. Push to the branch (`git push origin my-new-feature`)
61
+ 5. Create a new Pull Request
62
+
63
+ ## Authors
64
+
65
+ Created and maintained by [Fletcher Nichol][fnichol] (<fnichol@nichol.ca>) and
66
+ a growing community of [contributors][contributors].
67
+
68
+ ## License
69
+
70
+ Apache License, Version 2.0 (see [LICENSE.txt][license])
71
+
72
+ [command_executor]: https://github.com/test-kitchen/winrm-transport/blob/master/lib/winrm/transport/command_executor.rb
73
+ [contributors]: https://github.com/test-kitchen/winrm-transport/graphs/contributors
74
+ [file_transporter]: https://github.com/test-kitchen/winrm-transport/blob/master/lib/winrm/transport/file_transporter.rb
75
+ [fnichol]: https://github.com/fnichol
76
+ [issues]: https://github.com/test-kitchen/winrm-transpor/issues
77
+ [license]: https://github.com/test-kitchen/winrm-transport/blob/master/LICENSE.txt
78
+ [repo]: https://github.com/test-kitchen/winrm-transport
79
+ [semver]: http://semver.org/
80
+ [test_kitchen]: http://kitchen.ci
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ require "rake/testtask"
6
+ Rake::TestTask.new(:unit) do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList["spec/**/*_spec.rb"]
9
+ t.verbose = true
10
+ end
11
+
12
+ desc "Run all test suites"
13
+ task :test => [:unit]
14
+
15
+ desc "Display LOC stats"
16
+ task :stats do
17
+ puts "\n## Production Code Stats"
18
+ sh "countloc -r lib"
19
+ puts "\n## Test Code Stats"
20
+ sh "countloc -r spec"
21
+ end
22
+
23
+ require "finstyle"
24
+ require "rubocop/rake_task"
25
+ RuboCop::RakeTask.new(:style) do |task|
26
+ task.options << "--display-cop-names"
27
+ end
28
+
29
+ require "cane/rake_task"
30
+ desc "Run cane to check quality metrics"
31
+ Cane::RakeTask.new do |cane|
32
+ cane.canefile = "./.cane"
33
+ end
34
+
35
+ desc "Run all quality tasks"
36
+ task :quality => [:cane, :style, :stats]
37
+
38
+ require "yard"
39
+ YARD::Rake::YardocTask.new
40
+
41
+ desc "Generate gem dependency graph"
42
+ task :viz do
43
+ Bundler.with_clean_env do
44
+ sh "bundle viz --without test development guard " \
45
+ "--requirements --version"
46
+ end
47
+ end
48
+
49
+ task :default => [:test, :quality]
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "winrm/transport"
5
+
6
+ require "pry"
7
+ Pry.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "winrm/transport/version"
20
+
21
+ module WinRM
22
+
23
+ # WinRM transport logic for re-using remote shells and uploading files.
24
+ #
25
+ # @author Fletcher Nichol <fnichol@nichol.ca>
26
+ module Transport
27
+ end
28
+ end
@@ -0,0 +1,217 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Matt Wrock (<matt@mattwrock.com>)
4
+ #
5
+ # Copyright (C) 2014, Matt Wrock
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "winrm/transport/logging"
20
+
21
+ module WinRM
22
+
23
+ module Transport
24
+
25
+ # Object which can execute multiple commands and Powershell scripts in
26
+ # one shared remote shell session. The maximum number of commands per
27
+ # shell is determined by interrogating the remote host when the session
28
+ # is opened and the remote shell is automatically recycled before the
29
+ # threshold is reached.
30
+ #
31
+ # @author Matt Wrock <matt@mattwrock.com>
32
+ # @author Fletcher Nichol <fnichol@nichol.ca>
33
+ class CommandExecutor
34
+
35
+ include Logging
36
+
37
+ # @return [Integer,nil] the safe maximum number of commands that can
38
+ # be executed in one remote shell session, or `nil` if the
39
+ # threshold has not yet been determined
40
+ attr_reader :max_commands
41
+
42
+ # @return [String,nil] the identifier for the current open remote
43
+ # shell session, or `nil` if the session is not open
44
+ attr_reader :shell
45
+
46
+ # Creates a CommandExecutor given a `WinRM::WinRMWebService` object.
47
+ #
48
+ # @param service [WinRM::WinRMWebService] a winrm web service object
49
+ # @param logger [#debug,#info] an optional logger/ui object that
50
+ # responds to `#debug` and `#info` (default: `nil`)
51
+ # @param closer [ShellCloser] an optional object to automatically
52
+ # close the active open remote shell when CommandExecutor garbarge
53
+ # collects
54
+ def initialize(service, logger = nil, closer = nil)
55
+ @service = service
56
+ @logger = logger
57
+ @closer = closer
58
+ @command_count = 0
59
+ end
60
+
61
+ # Closes the open remote shell session. This method can be called
62
+ # multiple times, even if there is no open session.
63
+ def close
64
+ return if shell.nil?
65
+
66
+ service.close_shell(shell)
67
+ remove_finalizer
68
+ @shell = nil
69
+ end
70
+
71
+ # Opens a remote shell session for reuse. The maxiumum
72
+ # command-per-shell threshold is also determined the first time this
73
+ # method is invoked and cached for later invocations.
74
+ #
75
+ # @return [String] the remote shell session indentifier
76
+ def open
77
+ close
78
+ @shell = service.open_shell
79
+ add_finalizer(shell)
80
+ @command_count = 0
81
+ determine_max_commands unless max_commands
82
+ shell
83
+ end
84
+
85
+ # Runs a CMD command.
86
+ #
87
+ # @param command [String] the command to run on the remote system
88
+ # @param arguments [Array<String>] arguments to the command
89
+ # @yield [stdout, stderr] yields more live access the standard
90
+ # output and standard error streams as they are returns, if
91
+ # streaming behavior is desired
92
+ # @return [WinRM::Output] output object with stdout, stderr, and
93
+ # exit code
94
+ def run_cmd(command, arguments = [], &block)
95
+ reset if command_count_exceeded?
96
+ ensure_open_shell!
97
+
98
+ @command_count += 1
99
+ result = nil
100
+ service.run_command(shell, command, arguments) do |command_id|
101
+ result = service.get_command_output(shell, command_id, &block)
102
+ end
103
+ result
104
+ end
105
+
106
+ # Run a Powershell script that resides on the local box.
107
+ #
108
+ # @param script_file [IO,String] an IO reference for reading the
109
+ # Powershell script or the actual file contents
110
+ # @yield [stdout, stderr] yields more live access the standard
111
+ # output and standard error streams as they are returns, if
112
+ # streaming behavior is desired
113
+ # @return [WinRM::Output] output object with stdout, stderr, and
114
+ # exit code
115
+ def run_powershell_script(script_file, &block)
116
+ # this code looks overly compact in an attempt to limit local
117
+ # variable assignments that may contain large strings and
118
+ # consequently bloat the Ruby VM
119
+ run_cmd(
120
+ "powershell",
121
+ [
122
+ "-encodedCommand",
123
+ ::WinRM::PowershellScript.new(
124
+ script_file.is_a?(IO) ? script_file.read : script_file
125
+ ).encoded
126
+ ],
127
+ &block
128
+ )
129
+ end
130
+
131
+ private
132
+
133
+ # @return [Integer] the default maximum number of commands which can be
134
+ # executed in one remote shell session on "older" versions of Windows
135
+ # @api private
136
+ LEGACY_LIMIT = 15
137
+
138
+ # @return [Integer] the default maximum number of commands which can be
139
+ # executed in one remote shell session on "modern" versions of Windows
140
+ # @api private
141
+ MODERN_LIMIT = 1500
142
+
143
+ # @return [String] the PowerShell command used to determine the version
144
+ # of Windows
145
+ # @api private
146
+ PS1_OS_VERSION = "[environment]::OSVersion.Version.tostring()".freeze
147
+
148
+ # @return [Integer] the number of executed commands on the remote
149
+ # shell session
150
+ # @api private
151
+ attr_accessor :command_count
152
+
153
+ # @return [#debug,#info] the logger
154
+ # @api private
155
+ attr_reader :logger
156
+
157
+ # @return [WinRM::WinRMWebService] a WinRM web service object
158
+ # @api private
159
+ attr_reader :service
160
+
161
+ # Creates a finalizer for this connection which will close the open
162
+ # remote shell session when the object is garabage collected or on
163
+ # Ruby VM shutdown.
164
+ #
165
+ # @param shell_id [String] the remote shell identifier
166
+ # @api private
167
+ def add_finalizer(shell_id)
168
+ ObjectSpace.define_finalizer(self, @closer.for(shell_id)) if @closer
169
+ end
170
+
171
+ # @return [true,false] whether or not the number of exeecuted commands
172
+ # have exceeded the maxiumum threshold
173
+ # @api private
174
+ def command_count_exceeded?
175
+ command_count > max_commands.to_i
176
+ end
177
+
178
+ # Ensures that there is an open remote shell session.
179
+ #
180
+ # @raise [WinRM::WinRMError] if there is no open shell
181
+ # @api private
182
+ def ensure_open_shell!
183
+ if shell.nil?
184
+ raise ::WinRM::WinRMError, "#{self.class}#open must be called " \
185
+ "before any run methods are invoked"
186
+ end
187
+ end
188
+
189
+ # Determines the safe maximum number of commands that can be executed
190
+ # on a remote shell session by interrogating the remote host.
191
+ #
192
+ # @api private
193
+ def determine_max_commands
194
+ os_version = run_powershell_script(PS1_OS_VERSION).stdout.chomp
195
+ @max_commands = os_version < "6.2" ? LEGACY_LIMIT : MODERN_LIMIT
196
+ @max_commands -= 2 # to be safe
197
+ end
198
+
199
+ # Removes any finalizers for this connection.
200
+ #
201
+ # @api private
202
+ def remove_finalizer
203
+ ObjectSpace.undefine_finalizer(self) if @closer
204
+ end
205
+
206
+ # Closes the remote shell session and opens a new one.
207
+ #
208
+ # @api private
209
+ def reset
210
+ debug {
211
+ "Resetting WinRM shell (Max command limit is #{max_commands})"
212
+ }
213
+ open
214
+ end
215
+ end
216
+ end
217
+ end