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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a719c4faff9f018cf95614e68886b53475218bb27a73cde4579bbf7eb90d638b
4
+ data.tar.gz: f66e7025b43d7d343f269c076824eda4061b7565b418ee3637a57e17c404d9d6
5
+ SHA512:
6
+ metadata.gz: a078c9a5e63f20059d6148e48356efead6f2520f47d0225c94a33cf3721b5294f9f6e893e166e53fb4a4609f3e5c62dee9872ea023d460d0731581d812eeb9bf
7
+ data.tar.gz: b7595d1ab8c6c7df4506e590a19a6ab0653d27edcfec4a1eef1764c21e3d9eb2d2edaffae6beff4bbf09465b2d84d029bf15c89a7a5cb944104e712543ea5347
@@ -0,0 +1,35 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 2.7.2
14
+ - name: Run unit tests and code linting
15
+ run: |
16
+ gem install bundler -v 2.2.9
17
+ bundle install
18
+ bundle exec rake
19
+ bundle exec yard
20
+
21
+ - name: Deploy documentation
22
+ if: github.ref == 'refs/heads/main'
23
+ uses: crazy-max/ghaction-github-pages@v2.2.0
24
+ with:
25
+ target_branch: gh-pages
26
+ build_dir: doc
27
+ env:
28
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
29
+
30
+ - name: Release Gem
31
+ if: contains(github.ref, 'refs/tags/v')
32
+ uses: dawidd6/action-publish-gem@v1.2.0
33
+ with:
34
+ github_token: ${{secrets.GITHUB_TOKEN}}
35
+ api_key: ${{secrets.RUBYGEMS_API_KEY}}
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,41 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Layout/LineLength:
14
+ Max: 150
15
+
16
+ Metrics/ClassLength:
17
+ Max: 250
18
+
19
+ Metrics/BlockLength:
20
+ Exclude:
21
+ - spec/**/*.rb
22
+ - remotus.gemspec
23
+ - lib/remotus/ssh_connection.rb
24
+
25
+ Metrics/AbcSize:
26
+ Max: 25
27
+ Exclude:
28
+ - lib/remotus/ssh_connection.rb
29
+
30
+ Metrics/MethodLength:
31
+ Max: 20
32
+ Exclude:
33
+ - lib/remotus/ssh_connection.rb
34
+
35
+ Metrics/CyclomaticComplexity:
36
+ Exclude:
37
+ - lib/remotus/ssh_connection.rb
38
+
39
+ Metrics/PerceivedComplexity:
40
+ Exclude:
41
+ - lib/remotus/ssh_connection.rb
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-03-09
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in remotus.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,103 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ remotus (0.1.0)
5
+ connection_pool (~> 2.2)
6
+ net-scp (~> 3.0)
7
+ net-ssh (~> 6.1)
8
+ winrm (~> 2.3)
9
+ winrm-fs (~> 1.3)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ ast (2.4.2)
15
+ builder (3.2.4)
16
+ connection_pool (2.2.3)
17
+ diff-lcs (1.4.4)
18
+ erubi (1.10.0)
19
+ ffi (1.15.0)
20
+ gssapi (1.3.1)
21
+ ffi (>= 1.0.1)
22
+ gyoku (1.3.1)
23
+ builder (>= 2.1.2)
24
+ httpclient (2.8.3)
25
+ little-plugger (1.1.4)
26
+ logging (2.3.0)
27
+ little-plugger (~> 1.1)
28
+ multi_json (~> 1.14)
29
+ multi_json (1.15.0)
30
+ net-scp (3.0.0)
31
+ net-ssh (>= 2.6.5, < 7.0.0)
32
+ net-ssh (6.1.0)
33
+ nori (2.6.0)
34
+ parallel (1.20.1)
35
+ parser (3.0.0.0)
36
+ ast (~> 2.4.1)
37
+ rainbow (3.0.0)
38
+ rake (13.0.3)
39
+ regexp_parser (2.1.1)
40
+ rexml (3.2.4)
41
+ rspec (3.10.0)
42
+ rspec-core (~> 3.10.0)
43
+ rspec-expectations (~> 3.10.0)
44
+ rspec-mocks (~> 3.10.0)
45
+ rspec-core (3.10.1)
46
+ rspec-support (~> 3.10.0)
47
+ rspec-expectations (3.10.1)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.10.0)
50
+ rspec-mocks (3.10.2)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.10.0)
53
+ rspec-support (3.10.2)
54
+ rubocop (1.11.0)
55
+ parallel (~> 1.10)
56
+ parser (>= 3.0.0.0)
57
+ rainbow (>= 2.2.2, < 4.0)
58
+ regexp_parser (>= 1.8, < 3.0)
59
+ rexml
60
+ rubocop-ast (>= 1.2.0, < 2.0)
61
+ ruby-progressbar (~> 1.7)
62
+ unicode-display_width (>= 1.4.0, < 3.0)
63
+ rubocop-ast (1.4.1)
64
+ parser (>= 2.7.1.5)
65
+ rubocop-rake (0.5.1)
66
+ rubocop
67
+ rubocop-rspec (2.2.0)
68
+ rubocop (~> 1.0)
69
+ rubocop-ast (>= 1.1.0)
70
+ ruby-progressbar (1.11.0)
71
+ rubyntlm (0.6.3)
72
+ rubyzip (2.3.0)
73
+ unicode-display_width (2.0.0)
74
+ winrm (2.3.6)
75
+ builder (>= 2.1.2)
76
+ erubi (~> 1.8)
77
+ gssapi (~> 1.2)
78
+ gyoku (~> 1.0)
79
+ httpclient (~> 2.2, >= 2.2.0.2)
80
+ logging (>= 1.6.1, < 3.0)
81
+ nori (~> 2.0)
82
+ rubyntlm (~> 0.6.0, >= 0.6.3)
83
+ winrm-fs (1.3.5)
84
+ erubi (~> 1.8)
85
+ logging (>= 1.6.1, < 3.0)
86
+ rubyzip (~> 2.0)
87
+ winrm (~> 2.0)
88
+ yard (0.9.26)
89
+
90
+ PLATFORMS
91
+ x86_64-linux
92
+
93
+ DEPENDENCIES
94
+ rake (~> 13.0)
95
+ remotus!
96
+ rspec (~> 3.0)
97
+ rubocop (~> 1.7)
98
+ rubocop-rake (~> 0.5)
99
+ rubocop-rspec (~> 2.2)
100
+ yard (~> 0.9)
101
+
102
+ BUNDLED WITH
103
+ 2.2.9
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Matthew Newell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # Remotus
2
+
3
+ Remotus provides a simple ruby interface for pooling remote SSH and WinRM connections and managing remote credentials. Custom authentication stores may be added to allow remote credentials to be automatically retrieved.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'remotus'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install remotus
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ # Initialize a new connection pool to remotehost.local and auto-determine whether to use SSH or WinRM
25
+ connection = Remotus.connect("remotehost.local")
26
+
27
+ # Initialize a new connection pool to remotehost.local with a defined protocol and port
28
+ connection = Remotus.connect("remotehost.local", proto: :ssh, port: 2222)
29
+
30
+ # Create a credential for the new connection pool
31
+ connection.credential = Remotus::Auth::Credential.new("username", "password")
32
+
33
+ # Run a command on the remote host
34
+ result = connection.run("hostname")
35
+
36
+ result.command
37
+ # => "hostname"
38
+
39
+ result.stdout
40
+ # => "remotehost.local\n"
41
+
42
+ result.stderr
43
+ # => ""
44
+
45
+ result.output
46
+ # => "remotehost.local\n"
47
+
48
+ result.exit_code
49
+ # => 0
50
+
51
+ # Run a command on the remote host with sudo (Linux only, requires password to be specified)
52
+ result = connection.run("ls /root", sudo: true)
53
+
54
+ # Run a script on the remote host
55
+ connection.run_script("/local/script.sh", "/remote/path/script.sh")
56
+
57
+ # Run a script on the remote host with arguments
58
+ connection.run_script("/local/script.sh", "/remote/path/script.sh", "arg1", "arg2")
59
+
60
+ # Upload a file to the remote host
61
+ connection.upload("/local/file.txt", "/remote/path/file.txt")
62
+
63
+ # Download a file from the remote host
64
+ connection.download("/remote/path/file.txt", "/local/file.txt")
65
+ ```
66
+
67
+ Full documentation is available in the [Remotus GitHub Pages](https://wheatevo.github.io/remotus/).
68
+
69
+ ### Extending Remotus
70
+
71
+ Remotus may be extended by adding more authentication stores to gather remote credential data from centralized services.
72
+
73
+ #### Authentication Stores
74
+
75
+ More authentication stores may be added by creating a new class that inherits from `Remotus::Auth::Store` and implementing the `credential` method. The `credential` method should receive a `Remotus::SshConnection` or `Remotus::WinrmConnection` and an options hash and return a `Remotus::Auth::Credential` or `nil` if no credential can be found.
76
+
77
+ Once a new authentication store has been defined, ensure it is used by Remotus by adding it to `Remotus::Auth.stores`.
78
+
79
+ ```ruby
80
+ # Define a new authentication store that returns "<hostname>_password" for any connection
81
+ #
82
+ # This is for demonstration purposes only, please do not use any password with your hostname or "password" in it :)
83
+ require "remotus"
84
+
85
+ class SimpleStore < Remotus::Auth::Store
86
+ def credential(connection, **options)
87
+ "#{connection.host}_password"
88
+ end
89
+ end
90
+
91
+ # Add the authentication store to the array of existing authentication stores
92
+ Remotus::Auth.stores << SimpleStore.new
93
+
94
+ # Use the authentication store
95
+ connection = Remotus.connect("remotehost.local", proto: :ssh)
96
+ Remotus::Auth.credential(connection)
97
+ # => "remotehost.local_password"
98
+ ```
99
+
100
+ ## Development
101
+
102
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
103
+
104
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
105
+
106
+ ## Contributing
107
+
108
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wheatevo/remotus.
109
+
110
+ ## License
111
+
112
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "remotus"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/remotus.rb ADDED
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "remotus/version"
4
+ require "remotus/logger"
5
+ require "remotus/pool"
6
+ require "remotus/auth"
7
+
8
+ # Contains classes and methods for creating remote connections and running commands
9
+ module Remotus
10
+ #
11
+ # Creates/gets a host pool for the host that can be used to perform remote operations.
12
+ # After creation, the host pool will persist for future connections.
13
+ #
14
+ # @param [String] host hostname
15
+ # @param [Hash] options options hash
16
+ # @option options [Integer] :size number of connections in the pool
17
+ # @option options [Integer] :timeout amount of time to wait for a connection from the pool
18
+ # @option options [Integer] :port port to use for the connection
19
+ # @option options [Symbol] :proto protocol to use for the connection (:winrm, :ssh), must be specified if port is specified
20
+ #
21
+ # @return [Remotus::HostPool] Newly created or retrieved host pool
22
+ #
23
+ def self.connect(host, **options)
24
+ Remotus::Pool.connect(host, **options)
25
+ end
26
+
27
+ #
28
+ # Number of host pools in the connection pool
29
+ #
30
+ # @return [Integer] number of host pools
31
+ #
32
+ def self.count
33
+ Remotus::Pool.count
34
+ end
35
+
36
+ #
37
+ # Removes all host pools from the pool in a thread-safe manner
38
+ #
39
+ # @return [Integer] number of host pools removed
40
+ #
41
+ def self.clear
42
+ Remotus::Pool.clear
43
+ end
44
+
45
+ #
46
+ # Checks if a remote port is open
47
+ #
48
+ # @param [String] host remote host
49
+ # @param [Integer] port remote port
50
+ # @param [Integer] timeout amount of time to wait in seconds
51
+ #
52
+ # @return [Boolean] true if port is open, false otherwise
53
+ #
54
+ def self.port_open?(host, port, timeout: 1)
55
+ logger.debug { "Checking if #{host}:#{port} is accessible" }
56
+ Socket.tcp(host, port, connect_timeout: timeout) { true }
57
+ rescue StandardError
58
+ logger.debug { "#{host}:#{port} is inaccessible" }
59
+ false
60
+ end
61
+
62
+ #
63
+ # Determine remote host type by checking common ports
64
+ #
65
+ # @param [String] host hostname
66
+ # @param [Integer] timeout amount of time to wait in seconds
67
+ #
68
+ # @return [Symbol, nil] :ssh, :winrm, or nil if it cannot be determined
69
+ #
70
+ def self.host_type(host, timeout: 1)
71
+ return :ssh if port_open?(host, SshConnection::REMOTE_PORT, timeout: timeout)
72
+
73
+ return :winrm if port_open?(host, WinrmConnection::REMOTE_PORT, timeout: timeout)
74
+
75
+ nil
76
+ end
77
+
78
+ #
79
+ # Gets the remotus logger
80
+ #
81
+ # @return [Remotus::Logger] current logger
82
+ #
83
+ def self.logger
84
+ @logger ||= Remotus::Logger.new($stdout, level: Logger::INFO)
85
+ end
86
+
87
+ #
88
+ # Sets the remotus logger
89
+ #
90
+ # @param [::Logger] logger logger to set
91
+ #
92
+ def self.logger=(logger)
93
+ if logger.nil?
94
+ self.logger.level = Logger::FATAL
95
+ return self.logger
96
+ end
97
+ @logger = logger
98
+ end
99
+
100
+ #
101
+ # Gets the remotus log formatter
102
+ #
103
+ # @return [::Logger::Formatter] current log formatter
104
+ #
105
+ def self.log_formatter
106
+ @log_formatter ||= logger.formatter
107
+ end
108
+
109
+ #
110
+ # Sets the remotus log formatter
111
+ #
112
+ # @param [::Logger::Formatter] log_formatter new log formatter
113
+ #
114
+ def self.log_formatter=(log_formatter)
115
+ @log_formatter = log_formatter
116
+ logger.formatter = log_formatter
117
+ end
118
+
119
+ # Generic base class for Remotus errors
120
+ class Error < StandardError; end
121
+
122
+ # Failed to obtain PTY during SSH connection
123
+ class PtyError < Error; end
124
+
125
+ # Failed to determine remote node host type
126
+ class HostTypeDeterminationError < Error; end
127
+
128
+ # Failed to authenticate to remote host
129
+ class AuthenticationError < Error; end
130
+
131
+ # Raised when a RemoteCommand::Result has an error
132
+ class ResultError < Error; end
133
+
134
+ # Raised when a derived class is missing an override method
135
+ class MissingOverride < Error; end
136
+
137
+ # Failed to find credential in credential store
138
+ class MissingCredential < Error; end
139
+
140
+ # Failed to find credential password when executing sudo command
141
+ class MissingSudoPassword < Error; end
142
+ end