remotus 0.1.0

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.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Remotus
4
+ # Remotus gem version
5
+ VERSION = "0.1.0"
6
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "remotus"
4
+ require "remotus/result"
5
+ require "remotus/auth"
6
+ require "winrm"
7
+ require "winrm-fs"
8
+
9
+ module Remotus
10
+ # Class representing a WinRM connection to a host
11
+ class WinrmConnection
12
+ # Standard WinRM remote port
13
+ REMOTE_PORT = 5985
14
+
15
+ # @return [Integer] Remote port
16
+ attr_reader :port
17
+
18
+ # @return [String] host hostname
19
+ attr_reader :host
20
+
21
+ #
22
+ # Creates a WinrmConnection
23
+ #
24
+ # @param [String] host hostname
25
+ # @param [Integer] port remote port
26
+ #
27
+ def initialize(host, port = REMOTE_PORT)
28
+ @host = host
29
+ @port = port
30
+ end
31
+
32
+ #
33
+ # Connection type
34
+ #
35
+ # @return [Symbol] returns :winrm
36
+ #
37
+ def type
38
+ :winrm
39
+ end
40
+
41
+ #
42
+ # Retrieves/creates the base WinRM connection for the host
43
+ # If the base connection already exists, the existing connection will be retrieved
44
+ #
45
+ # @return [WinRM::Connection] base WinRM remote connection
46
+ #
47
+ def base_connection(reload: false)
48
+ return @base_connection if !reload && !restart_base_connection?
49
+
50
+ Remotus.logger.debug { "Initializing WinRM connection to #{Remotus::Auth.credential(self).user}@#{@host}:#{@port}" }
51
+ @base_connection = WinRM::Connection.new(
52
+ endpoint: "http://#{@host}:#{@port}/wsman",
53
+ transport: :negotiate,
54
+ user: Remotus::Auth.credential(self).user,
55
+ password: Remotus::Auth.credential(self).password
56
+ )
57
+ end
58
+
59
+ #
60
+ # Retrieves/creates the WinRM shell connection for the host
61
+ # If the connection already exists, the existing connection will be retrieved
62
+ #
63
+ # @return [WinRM::Shells::Powershell] remote connection
64
+ #
65
+ def connection
66
+ return @connection unless restart_connection?
67
+
68
+ @connection = base_connection(reload: true).shell(:powershell)
69
+ end
70
+
71
+ #
72
+ # Whether the remote host's WinRM port is available
73
+ #
74
+ # @return [Boolean] true if available, false otherwise
75
+ #
76
+ def port_open?
77
+ Remotus.port_open?(@host, @port)
78
+ end
79
+
80
+ #
81
+ # Runs a command on the host
82
+ #
83
+ # @param [String] command command to run
84
+ # @param [Array] args command arguments
85
+ # @param [Hash] _options unused command options
86
+ #
87
+ # @return [Remotus::Result] result describing the stdout, stderr, and exit status of the command
88
+ #
89
+ def run(command, *args, **_options)
90
+ command = "#{command}#{args.empty? ? "" : " "}#{args.join(" ")}"
91
+ run_result = connection.run(command)
92
+ Remotus::Result.new(command, run_result.stdout, run_result.stderr, run_result.output, run_result.exitcode)
93
+ rescue WinRM::WinRMAuthorizationError => e
94
+ raise Remotus::AuthenticationError, e.to_s
95
+ end
96
+
97
+ #
98
+ # Uploads a script and runs it on the host
99
+ #
100
+ # @param [String] local_path local path of the script (source)
101
+ # @param [String] remote_path remote path for the script (destination)
102
+ # @param [Array] args script arguments
103
+ # @param [Hash] options command options
104
+ #
105
+ # @return [Remotus::Result] result describing the stdout, stderr, and exit status of the command
106
+ #
107
+ def run_script(local_path, remote_path, *args, **options)
108
+ upload(local_path, remote_path)
109
+ run(remote_path, *args, **options)
110
+ end
111
+
112
+ #
113
+ # Uploads a file from the local host to the remote host
114
+ #
115
+ # @param [String] local_path local path to upload the file from (source)
116
+ # @param [String] remote_path remote path to upload the file to (destination)
117
+ # @param [Hash] _options unused upload options
118
+ #
119
+ # @return [String] remote path
120
+ #
121
+ def upload(local_path, remote_path, _options = {})
122
+ Remotus.logger.debug { "Uploading file #{local_path} to #{@host}:#{remote_path}" }
123
+ WinRM::FS::FileManager.new(base_connection).upload(local_path, remote_path)
124
+ remote_path
125
+ end
126
+
127
+ #
128
+ # Downloads a file from the remote host to the local host
129
+ #
130
+ # @param [String] remote_path remote path to download the file from (source)
131
+ # @param [String] local_path local path to download the file to (destination)
132
+ # @param [Hash] _options unused download options
133
+ #
134
+ # @return [String] local path
135
+ #
136
+ def download(remote_path, local_path, _options = {})
137
+ Remotus.logger.debug { "Downloading file #{local_path} from #{@host}:#{remote_path}" }
138
+ WinRM::FS::FileManager.new(base_connection).download(remote_path, local_path)
139
+ local_path
140
+ end
141
+
142
+ #
143
+ # Checks if a remote file or directory exists
144
+ #
145
+ # @param [String] remote_path remote path to the file or directory
146
+ # @param [Hash] _options unused command options
147
+ #
148
+ # @return [Boolean] true if the file or directory exists, false otherwise
149
+ #
150
+ def file_exist?(remote_path, **_options)
151
+ Remotus.logger.debug { "Checking if file #{remote_path} exists on #{@host}" }
152
+ WinRM::FS::FileManager.new(base_connection).exists?(remote_path)
153
+ end
154
+
155
+ private
156
+
157
+ #
158
+ # Whether to restart the current WinRM base connection
159
+ #
160
+ # @return [Boolean] whether to restart the current base connection
161
+ #
162
+ def restart_base_connection?
163
+ return restart_connection? if @connection
164
+ return true unless @base_connection
165
+ return true if @host != @base_connection.instance_values["connection_opts"][:endpoint].scan(%r{//(.*):}).flatten.first
166
+ return true if Remotus::Auth.credential(self).user != @base_connection.instance_values["connection_opts"][:user]
167
+ return true if Remotus::Auth.credential(self).password != @base_connection.instance_values["connection_opts"][:password]
168
+
169
+ false
170
+ end
171
+
172
+ #
173
+ # Whether to restart the current WinRM connection
174
+ #
175
+ # @return [Boolean] whether to restart the current connection
176
+ #
177
+ def restart_connection?
178
+ return true unless @connection
179
+ return true if @host != @connection.connection_opts[:endpoint].scan(%r{//(.*):}).flatten.first
180
+ return true if Remotus::Auth.credential(self).user != @connection.connection_opts[:user]
181
+ return true if Remotus::Auth.credential(self).password != @connection.connection_opts[:password]
182
+
183
+ false
184
+ end
185
+ end
186
+ end
data/remotus.gemspec ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/remotus/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "remotus"
7
+ spec.version = Remotus::VERSION
8
+ spec.authors = ["Matthew Newell"]
9
+ spec.email = ["matthewtnewell@gmail.com"]
10
+
11
+ spec.summary = "Ruby gem for connecting to remote systems seamlessly via WinRM or SSH."
12
+ spec.description = "Ruby gem for connecting to remote systems seamlessly via WinRM or SSH."
13
+ spec.homepage = "https://github.com/wheatevo/remotus"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/wheatevo/remotus"
19
+ spec.metadata["documentation_uri"] = "https://wheatevo.github.io/remotus/"
20
+ spec.metadata["changelog_uri"] = "https://github.com/wheatevo/remotus/blob/master/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ # Dependencies
32
+ spec.add_dependency "connection_pool", "~> 2.2"
33
+ spec.add_dependency "net-scp", "~> 3.0"
34
+ spec.add_dependency "net-ssh", "~> 6.1"
35
+ spec.add_dependency "winrm", "~> 2.3"
36
+ spec.add_dependency "winrm-fs", "~> 1.3"
37
+
38
+ # Development dependencies
39
+ spec.add_development_dependency "rake", "~> 13.0"
40
+ spec.add_development_dependency "rspec", "~> 3.0"
41
+ spec.add_development_dependency "rubocop", "~> 1.7"
42
+ spec.add_development_dependency "rubocop-rake", "~> 0.5"
43
+ spec.add_development_dependency "rubocop-rspec", "~> 2.2"
44
+ spec.add_development_dependency "yard", "~> 0.9"
45
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remotus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Newell
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: connection_pool
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-scp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-ssh
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '6.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '6.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: winrm
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: winrm-fs
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '13.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '13.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.7'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.7'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.5'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.5'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.2'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.2'
153
+ - !ruby/object:Gem::Dependency
154
+ name: yard
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.9'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.9'
167
+ description: Ruby gem for connecting to remote systems seamlessly via WinRM or SSH.
168
+ email:
169
+ - matthewtnewell@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".github/workflows/main.yml"
175
+ - ".gitignore"
176
+ - ".rspec"
177
+ - ".rubocop.yml"
178
+ - CHANGELOG.md
179
+ - Gemfile
180
+ - Gemfile.lock
181
+ - LICENSE.txt
182
+ - README.md
183
+ - Rakefile
184
+ - bin/console
185
+ - bin/setup
186
+ - lib/remotus.rb
187
+ - lib/remotus/auth.rb
188
+ - lib/remotus/auth/credential.rb
189
+ - lib/remotus/auth/hash_store.rb
190
+ - lib/remotus/auth/store.rb
191
+ - lib/remotus/host_pool.rb
192
+ - lib/remotus/logger.rb
193
+ - lib/remotus/pool.rb
194
+ - lib/remotus/result.rb
195
+ - lib/remotus/ssh_connection.rb
196
+ - lib/remotus/version.rb
197
+ - lib/remotus/winrm_connection.rb
198
+ - remotus.gemspec
199
+ homepage: https://github.com/wheatevo/remotus
200
+ licenses:
201
+ - MIT
202
+ metadata:
203
+ homepage_uri: https://github.com/wheatevo/remotus
204
+ source_code_uri: https://github.com/wheatevo/remotus
205
+ documentation_uri: https://wheatevo.github.io/remotus/
206
+ changelog_uri: https://github.com/wheatevo/remotus/blob/master/CHANGELOG.md
207
+ post_install_message:
208
+ rdoc_options: []
209
+ require_paths:
210
+ - lib
211
+ required_ruby_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: 2.4.0
216
+ required_rubygems_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - ">="
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ requirements: []
222
+ rubygems_version: 3.1.4
223
+ signing_key:
224
+ specification_version: 4
225
+ summary: Ruby gem for connecting to remote systems seamlessly via WinRM or SSH.
226
+ test_files: []