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.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +35 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +41 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +103 -0
- data/LICENSE.txt +21 -0
- data/README.md +112 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/remotus.rb +142 -0
- data/lib/remotus/auth.rb +66 -0
- data/lib/remotus/auth/credential.rb +145 -0
- data/lib/remotus/auth/hash_store.rb +56 -0
- data/lib/remotus/auth/store.rb +43 -0
- data/lib/remotus/host_pool.rb +181 -0
- data/lib/remotus/logger.rb +9 -0
- data/lib/remotus/pool.rb +189 -0
- data/lib/remotus/result.rb +83 -0
- data/lib/remotus/ssh_connection.rb +447 -0
- data/lib/remotus/version.rb +6 -0
- data/lib/remotus/winrm_connection.rb +186 -0
- data/remotus.gemspec +45 -0
- metadata +226 -0
@@ -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: []
|