assemblr 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a56be997b4579f72b563f67e40f81a2aaaec974dc2568a345528e9171b0813c1
4
- data.tar.gz: 2d08815cb4b1c2d2245c75a9337a7586dfa2ef7e646ed726d8ed6527c90166c6
3
+ metadata.gz: 864016a190bb0067cd305a9824003a3cca4be4dd5eab9eb92b4f1e88ea388789
4
+ data.tar.gz: '08f75aa401dd8863a95897c07eacfffa5364b8cbefff9f39711167ff803867c9'
5
5
  SHA512:
6
- metadata.gz: 7571588a2718e3ed241d328b5d25d9fd9efb75054bee707c27d65b549e3dd944e909dc715d93958a0eeb18386cffead01d92306a800a3957d6b4dae5aeac9fa9
7
- data.tar.gz: cb10006737feca4ee02d42af22b6d06e9fc4fea7ae258d374f27e3363c37bcc03abf3877280cf29812d52ff9ae2fc2d4f5f5677752468dbca6d5f29b82f03a70
6
+ metadata.gz: 3d35cb5c921b0da7ffd6440aafe890e14c113dbee161be2351161c79c56f9ac10d228688c3bcbfe70181068ce159da0a7349032b6697b88d3eb57a71f1e3506f
7
+ data.tar.gz: 2b59abd44694032c93d6018b5698d9eb227fe5019c4aad8914805f32992bd7dfd60ab0015f4923e207b29f085bc44eafbc44c319057fa5286e8abe0260306ff5
data/.gitignore CHANGED
@@ -9,5 +9,7 @@
9
9
  /.idea/
10
10
  /examples/
11
11
 
12
+ *.gem
13
+
12
14
  /Gemfile.lock
13
15
  /.rakeTasks
data/README.md CHANGED
@@ -22,45 +22,33 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- This is an example of a simple assemblr script:
25
+ Assemblr is separated into modules. The core module contains simple logging
26
+ methods since they're needed by all other modules. It gets automatically
27
+ imported when requiring any sub-modules.
26
28
 
27
- ```ruby
28
- # frozen_string_literal: true
29
-
30
- require 'assemblr'
31
-
32
- # Configuration options. The values listed below are the defaults for assemblr.
33
- set :default_user, 'root' # set the default user nodes are assigned to
34
- set :default_group, 'default' # set the default group nodes are assigned to
35
- set :log, true # turn on or off built-in logging.
36
- set :quit_on_error, true # quit if anything goes wrong.
29
+ Here are the currently existing modules (although none of them are close to
30
+ finished):
37
31
 
38
- group :dev_servers do
39
- # Define nodes. All nodes defined in this block will be added to the
40
- # specified group (:dev_servers). After the block ends, the default user and
41
- # group will be restored for any other top-level node definitions.
42
- node '10.0.0.173'
43
- node '10.0.0.332'
44
- node '10.0.1.22'
45
- end
32
+ - `assemblr/shell`
33
+ - `assemblr/network`
34
+ - `assemblr/remote`
46
35
 
47
- node '10.0.0.33' # assigned to group :default and user 'root' as per the
48
- # defaults
36
+ There are currently two styles available to call the methods defined in these
37
+ modules:
49
38
 
50
- group :dev_servers do
51
- upload 'Gemfile', '/tmp/Gemmm' # upload a file to all nodes in a group
52
- upload 'lib', '/tmp',
53
- recursive: true # upload a directory to all nodes in a group
54
-
55
- remote_exec 'echo "Hello, world!"'
56
- end
39
+ ```ruby
40
+ require 'assemblr/shell'
57
41
 
58
- upload_to '10.0.2.43', 'root', 'message.txt', '/home/user/message.txt'
59
- result = remote_exec_on 'ruby', inject: ['puts "Hello, world!"', 'exit']
42
+ # Style 1
43
+ Shell.exec 'my_command'
60
44
 
61
- puts result
45
+ # Style 2
46
+ shell_exec 'my_command'
62
47
  ```
63
48
 
49
+ Both styles are automatically imported into the main scope when the file is
50
+ required.
51
+
64
52
  ## Development
65
53
 
66
54
  After checking out the repo, run `bin/setup` to install dependencies. You can
@@ -1,275 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'assemblr/version'
4
- require 'assemblr/logging'
5
- require 'assemblr/utilities'
3
+ $VERBOSE = nil
6
4
 
7
- require 'open3'
8
- require 'net/ssh'
9
- require 'net/scp'
5
+ require 'tty-logger'
10
6
 
7
+ ##
8
+ # Define the core Assemblr methods.
11
9
  module Assemblr
12
- $VERBOSE = nil # turn off warnings from external libraries
10
+ @@options = []
11
+ @@error_hook = -> {}
13
12
 
14
- # Local variables are prefixed with an 'm' for 'Module' as to not clash with
15
- # method names.
16
- m_nodes = []
17
- m_current_group = 'default'
18
- m_current_user = 'root'
13
+ def set_option(key, value)
14
+ return unless @@options.include?(key.to_sym)
19
15
 
20
- ##
21
- # Set a configuration option.
22
- #
23
- # Available options are:
24
- #
25
- # - *default_user* - the default user to assign nodes to
26
- # - *default_group* - the default group to assign nodes to
27
- # - *log* - turn on or off logging
28
- # - *quit_on_error* - quit if any error occur
29
- #
30
- # @return [void]
31
- define_method :set do |key, value|
32
- options = %i[log quit_on_error default_user default_group]
33
- unless options.include?(key)
34
- raise ArgumentError, "#{key} is not a valid option"
35
- end
36
-
37
- key = :current_user if key == :default_user
38
- key = :current_group if key == :default_group
39
-
40
- info %(config: set "#{key}" to "#{value}")
41
- eval "m_#{key} = value", binding, __FILE__, __LINE__
16
+ Assemblr.class_variable_set("@@#{key}", value)
42
17
  end
43
18
 
44
- ##
45
- # @!method local_exec(cmd, inject: [])
46
- # Execute a command locally.
47
- #
48
- # @param cmd [String] the command to run locally
49
- # @param inject [Array<String>] strings to inject into stdin
50
- #
51
- # @return [Array(String, Process::Status)]
52
- define_method :local_exec do |cmd, inject: []|
53
- log_string = "running `#{cmd}` locally"
54
- log_string += " with these inputs: #{inject}" unless inject.empty?
55
- info log_string
56
-
57
- result = ''
58
- status = nil
59
- Open3.popen2e(cmd) do |i, o, wait|
60
- inject.each do |line|
61
- line = line.strip
62
- i.write line + "\n"
63
- end
64
- i.close
65
- result = o.read
66
- status = wait.value
67
- end
68
-
69
- if status.exitstatus != 0
70
- code = status.exitstatus
71
- error "local command `#{cmd}` exited with a status of #{code}"
72
- else
73
- success "local command `#{cmd}` executed successfully"
74
- end
75
-
76
- return result, status
19
+ def get_option(key)
20
+ Assemblr.class_variable_get("@@#{key}")
77
21
  end
78
22
 
79
- ##
80
- # Execute a command on a remote machine using SSH. It returns the combined
81
- # stdout and stderr data.
82
- #
83
- # @param ip [String] the ip of the node to run the command on
84
- # @param user [String] the user to log in as
85
- # @param cmd [String] the command to execute remotely
86
- # @param inject [Array<String>] strings to inject into stdin
87
- #
88
- # @return [String]
89
- # @return [Array<String>]
90
- def remote_exec_on(ip, user, cmd, inject: [])
91
- info "attempting to execute `#{cmd}` on #{user}@#{ip}"
92
-
93
- result = ''
94
- Net::SSH.start(ip, user) do |ssh|
95
- ch = ssh.open_channel do |ch|
96
- ch.exec(cmd) do |ch, success|
97
- error "unable to execute `#{cmd}` on #{user}@#{ip}" unless success
98
-
99
- # Collect stdout data.
100
- ch.on_data do |_, data|
101
- result += data if data.is_a?(String)
102
- end
103
-
104
- # Collect stderr data.
105
- ch.on_extended_data do |_, _, data|
106
- result += data if data.is_a?(String)
107
- end
108
-
109
- inject.each do |line|
110
- ch.send_data(line + "\n")
111
- end
112
-
113
- ch.on_request 'exit-status' do |_, data|
114
- code = data.read_long
115
- if code.zero?
116
- success "`#{cmd}` executed successfully on #{user}@#{ip}"
117
- else
118
- error "`#{cmd}` returned status code #{code} on #{user}@#{ip}"
119
- end
120
- end
121
- end
122
- end
123
-
124
- ch.wait
125
- end
126
- result
23
+ def define_option(name, value)
24
+ Assemblr.class_variable_set("@@#{name}", value)
25
+ @@options << name.to_sym
127
26
  end
128
27
 
129
- ##
130
- # Upload a file to a node.
131
- #
132
- # @param ip [String] the node to upload to
133
- # @param user [String] the user to authenticate as
134
- # @param src [String] the file to upload
135
- # @param dest [String] the location to upload to
136
- # @param recursive [Bool] recursively upload files in a directory
137
- # @param timeout [Integer] how many seconds to wait before throwing an error
138
- #
139
- # @return [void]
140
- def upload_to(ip, user, src, dest, recursive: false, timeout: 5)
141
- info %(attempting to upload "#{src}" to #{user}@#{ip}:#{dest})
142
- Timeout.timeout timeout do
143
- Net::SCP.upload!(ip, user, src, dest, recursive: recursive)
144
- end
145
- rescue StandardError
146
- error %(failed to upload "#{src}" to #{user}@#{ip}:#{dest})
147
- rescue Timeout::Error
148
- error %(failed to upload "#{src}" to #{user}@#{ip}:#{dest} within #{timeout} seconds)
149
- else
150
- success %(uploaded "#{src}" to #{user}@#{ip}:#{dest})
28
+ def options
29
+ Assemblr.class_variable_get('@@options')
151
30
  end
152
31
 
153
- ##
154
- # @!method current_nodes()
155
- # Get all nodes in the current group.
156
- #
157
- # @return [void]
158
- define_method :current_nodes do
159
- return nodes if m_current_group == 'all'
160
-
161
- nodes.select { |n| n[:group] == m_current_group }
32
+ def error_hook
33
+ @@on_error
162
34
  end
163
35
 
164
- ##
165
- # Execute a command on all machines within the current group.
166
- #
167
- # @param cmd [String] the command to execute
168
- # @param inject [Array<String>] strings to inject into stdin
169
- #
170
- # @return [String] the combined output of the command
171
- def remote_exec(cmd, inject: [])
172
- results = []
173
- current_nodes.each do |n|
174
- result = remote_exec_on(n[:ip], n[:user], cmd, inject: inject)
175
- results << result
176
- end
177
- results
36
+ def on_error(&block)
37
+ @@on_error = block
178
38
  end
179
39
 
180
40
  ##
181
- # Upload a file or directory to all machines within the current group.
182
- #
183
- # @param src [String] the file to upload
184
- # @param dest [String] the location to upload the file to
185
- # @param recursive [Bool] recursively upload files in a directory
186
- # @param timeout [Integer] how many seconds to wait before throwing an error
187
- #
188
- # @return [void]
189
- def upload(src, dest, recursive: false, timeout: 5)
190
- current_nodes.each do |n|
191
- upload_to(n[:ip], n[:user], src, dest, recursive: recursive)
192
- end
41
+ # Generates method definitions in all supported styles.
42
+ def expose_method(name)
43
+ prefix = to_s.downcase.gsub('::', '_').delete_prefix('assemblr_')
44
+ eval "#{self}.define_method(:#{prefix}_#{name}, &#{self}.method(:#{name}))"
45
+ eval "#{self}.define_singleton_method(:#{name}, &#{self}.method(:#{name}))"
193
46
  end
47
+ end
194
48
 
195
- ##
196
- # @!method group(group, &block)
197
- # Temporarily change current_group to refer to the specified group while
198
- # within the passed block.
199
- #
200
- # @param group [Symbol] the group to temporarily switch to
201
- #
202
- # @return [void]
203
- define_method :group do |group, &block|
204
- info %(entered group block with group "#{group}")
205
-
206
- # Temporarily switch to the target group.
207
- old_current_group = m_current_group
208
- m_current_group = group.to_s
209
-
210
- block.call
211
-
212
- m_current_group = old_current_group
213
-
214
- info %(exited group block with group "#{group}")
215
- end
49
+ include Assemblr
216
50
 
51
+ module Assemblr
217
52
  ##
218
- # @!method current_group
219
- # Get the current default group.
220
- #
221
- # @return [String]
53
+ # Define some quick-and-easy logging methods.
54
+ module Log
55
+ define_option :log, true
56
+ define_option :log_error_hook, true
222
57
 
223
- ##
224
- # @!method current_user
225
- # Get the current default user.
226
- #
227
- # @return [String]
228
- %w[group user].each do |item|
229
- define_method "current_#{item}" do
230
- eval "m_current_#{item}"
58
+ TTYLogger = TTY::Logger.new do |config|
59
+ config.metadata = %i[time date]
231
60
  end
232
- end
233
61
 
234
- ##
235
- # @!method node(ip, group: current_group, user: current_user)
236
- # Add a node to the list of nodes.
237
- #
238
- # @param ip [String] the ip address of the node
239
- # @param group [Symbol] the group to assign the node to
240
- # @param user [String] the user to associate with the node
241
- #
242
- # @return [Hash{ip=>String, group=>String, user=>String}] the added node
243
- define_method :node do |ip, group: m_current_group, user: m_current_user|
244
- group = group.to_s
245
- if group == 'all'
246
- error = ArgumentError.new('nodes can not be assigned to the "all" group')
247
- raise error
62
+ def self.included(_)
63
+ expose_method :info
64
+ expose_method :success
65
+ expose_method :warn
66
+ expose_method :error
248
67
  end
249
68
 
250
- l_node = { ip: ip, group: group, user: user }
251
- m_nodes << l_node
252
- info "added node: #{l_node}"
253
-
254
- l_node
255
- end
256
-
257
- ##
258
- # @!method nodes()
259
- # Get all registered nodes.
260
- #
261
- # @return [Array<Hash{ip=>String, group=>String, user=>String}>]
262
- define_method :nodes do
263
- m_nodes
264
- end
69
+ class << self
70
+ ##
71
+ # @!method info(message)
72
+ # Logs an info message.
73
+ #
74
+ # @return [void]
75
+
76
+ ##
77
+ # @!method success(message)
78
+ # Logs a success message.
79
+ #
80
+ # @return [void]
81
+
82
+ ##
83
+ # @!method warn(message)
84
+ # Logs a warning message.
85
+ #
86
+ # @return [void]
87
+ %i[info success warn].each do |level|
88
+ define_method level do |*args, &block|
89
+ TTYLogger.send(level, *args, &block) if get_option(:log)
90
+ end
91
+ end
265
92
 
266
- define_method :nodes_reachable? do
267
- reachable = true
268
- m_nodes.each do |node|
269
- reachable &&= reachable?(node[:ip])
93
+ ##
94
+ # @!method error(message)
95
+ # Logs a error message. If configured, this will call the on_error proc
96
+ #
97
+ # @return [void]
98
+ define_method :error do |*args, &block|
99
+ TTYLogger.send(:error, *args, &block) if get_option(:log)
100
+ error_hook.call if get_option(:log_error_hook)
101
+ end
270
102
  end
271
- reachable
272
103
  end
273
104
  end
274
105
 
275
- extend Assemblr
106
+ include Assemblr::Log
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ $VERBOSE = nil
4
+
5
+ require 'assemblr'
6
+
7
+ require 'socket'
8
+ require 'net/ping'
9
+
10
+ module Assemblr
11
+ ##
12
+ # Defines methods for common network functions.
13
+ module Network
14
+ def self.included(_)
15
+ expose_method :local_ip
16
+ expose_method :ip_reachable?
17
+ end
18
+
19
+ class << self
20
+ ##
21
+ # Retrieve the first ip address that is not '127.0.0.1' on the local
22
+ # machine.
23
+ #
24
+ # @param pattern [Regexp] a pattern to test the IPs against
25
+ #
26
+ # @return [String]
27
+ def local_ip(pattern = //)
28
+ Socket.ip_address_list.each do |ip|
29
+ address = ip.ip_address
30
+ next if address == '127.0.0.1'
31
+ return address if address.match?(pattern)
32
+ end
33
+ ''
34
+ end
35
+
36
+ ##
37
+ # Check if a remote ip can be contacted.
38
+ #
39
+ # @return [Boolean]
40
+ def ip_reachable?(ip)
41
+ external = Net::Ping::External.new(ip)
42
+
43
+ log_info %(attempting to contact host "#{ip}")
44
+ reachable = external.ping || external.ping6
45
+ if reachable
46
+ log_success %(host "#{ip}" is reachable)
47
+ else
48
+ log_error %(unable to contact host "#{ip}")
49
+ end
50
+
51
+ reachable
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ include Assemblr::Network
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'assemblr'
4
+
5
+ require 'timeout'
6
+ require 'net/ssh'
7
+ require 'net/scp'
8
+
9
+ module Assemblr
10
+ ##
11
+ # Defines methods to handle interacting with remote nodes.
12
+ module Remote
13
+ @@nodes = []
14
+
15
+ define_option :remote_default_group, :default
16
+ define_option :remote_default_user, 'root'
17
+
18
+ def self.included(_)
19
+ expose_method :group
20
+ expose_method :upload
21
+ expose_method :exec
22
+ expose_method :current_nodes
23
+ expose_method :node
24
+ expose_method :nodes
25
+ end
26
+
27
+ class << self
28
+ ##
29
+ # Defines methods availabile only while in a group block.
30
+ module Group
31
+ def self.remote_upload(src, dest, recursive: false, timeout: 5)
32
+ Remote.current_nodes.each do |n|
33
+ Remote.upload(n[:ip], n[:user], src, dest,
34
+ recursive: recursive,
35
+ timeout: timeout)
36
+ end
37
+ end
38
+
39
+ def self.remote_exec(cmd, inject: [])
40
+ results = []
41
+ Remote.current_nodes.each do |n|
42
+ result = Remote.remote_exec(n[:ip], n[:user], cmd, inject: inject)
43
+ results << result
44
+ end
45
+ results
46
+ end
47
+ end
48
+
49
+ # Execute a command on a remote machine using SSH. It returns the
50
+ # combined stdout and stderr data.
51
+ #
52
+ # @param ip [String] the ip of the node to run the command on
53
+ # @param user [String] the user to log in as
54
+ # @param cmd [String] the command to execute remotely
55
+ # @param inject [Array<String>] strings to inject into stdin
56
+ #
57
+ # @return [String]
58
+ def exec(ip, user, cmd, inject: [])
59
+ log_info "attempting to execute `#{cmd}` on #{user}@#{ip}"
60
+
61
+ result = ''
62
+ exit_code = 0
63
+ Net::SSH.start(ip, user) do |ssh|
64
+ ch = ssh.open_channel do |ch|
65
+ ch.exec(cmd) do |ch, success|
66
+ log_error "unable to execute `#{cmd}` on #{user}@#{ip}" unless success
67
+
68
+ # Collect stdout data.
69
+ ch.on_data do |_, data|
70
+ result += data if data.is_a?(String)
71
+ end
72
+
73
+ # Collect stderr data.
74
+ ch.on_extended_data do |_, _, data|
75
+ result += data if data.is_a?(String)
76
+ end
77
+
78
+ inject.each do |line|
79
+ ch.send_data(line + "\n")
80
+ end
81
+
82
+ ch.on_request 'exit-status' do |_, data|
83
+ exit_code = data.read_long
84
+ if exit_code.zero?
85
+ log_success "`#{cmd}` executed successfully on #{user}@#{ip}"
86
+ else
87
+ log_error "`#{cmd}` returned status code #{exit_code} on #{user}@#{ip}"
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ ch.wait
94
+ end
95
+ [result, exit_code]
96
+ end
97
+
98
+ ##
99
+ # Return all nodes within the current default group.
100
+ #
101
+ # @return [Array<Hash{ip=>String, group=>String, user=>String}>]
102
+ def current_nodes
103
+ return nodes if get_option(:remote_default_group) == 'all'
104
+
105
+ @@nodes.select { |n| n[:group] == get_option(:remote_default_group).to_s }
106
+ end
107
+
108
+ ##
109
+ # Temporarily change the default group to refer to the specified group
110
+ # while within the passed block.
111
+ #
112
+ # This method also changes the behavior of `remote_exec` and
113
+ # `remote_upload` while within the given block. Both functions don't take
114
+ # an IP # and a user, as they collect the information for all nodes
115
+ # within the group. In that case, the return value is an array of the
116
+ # results given by each node.
117
+ #
118
+ # @param group [Symbol] the group to temporarily switch to
119
+ #
120
+ # @return [void]
121
+ def group(group, &block)
122
+ log_info %(entered group block with group "#{group}")
123
+
124
+ # Temporarily switch to the target group.
125
+ old_default_group = get_option(:remote_default_group)
126
+ set_option(:remote_default_group, group.to_s)
127
+
128
+ Group.class_eval(&block)
129
+
130
+ set_option(:remote_default_group, old_default_group)
131
+
132
+ log_info %(exited group block with group "#{group}")
133
+ end
134
+
135
+ ##
136
+ # Upload a file or directory to a node.
137
+ #
138
+ # @param ip [String] the node to upload to
139
+ # @param user [String] the user to authenticate as
140
+ # @param src [String] the file to upload
141
+ # @param dest [String] the location to upload to
142
+ # @param recursive [Bool] recursively upload files in a directory
143
+ # @param timeout [Integer] how many seconds to wait before throwing an error
144
+ #
145
+ # @return [void]
146
+ def upload(ip, user, src, dest, recursive: false, timeout: 5)
147
+ log_info %(attempting to upload "#{src}" to #{user}@#{ip}:#{dest})
148
+ Timeout.timeout timeout do
149
+ Net::SCP.upload!(ip, user, src, dest, recursive: recursive)
150
+ end
151
+ rescue Timeout::Error
152
+ log_error %(failed to upload "#{src}" to #{user}@#{ip}:#{dest} within #{timeout} seconds)
153
+ rescue StandardError
154
+ log_error %(failed to upload "#{src}" to #{user}@#{ip}:#{dest})
155
+ else
156
+ log_success %(uploaded "#{src}" to #{user}@#{ip}:#{dest})
157
+ end
158
+
159
+ ##
160
+ # Add a node to the list of nodes.
161
+ #
162
+ # @param ip [String] the ip address of the node
163
+ # @param group [Symbol] the group to assign the node to
164
+ # @param user [String] the user to associate with the node
165
+ #
166
+ # @return [Hash{ip=>String, group=>String, user=>String}] the added node
167
+ def node(ip,
168
+ group: get_option(:remote_default_group),
169
+ user: get_option(:remote_default_user))
170
+ group = group.to_s
171
+ if group == 'all'
172
+ message = 'nodes can not be assigned to the "all" group'
173
+ error = ArgumentError.new(message)
174
+ raise error
175
+ end
176
+
177
+ l_node = { ip: ip, group: group, user: user }
178
+ @@nodes << l_node
179
+ log_info "added node: #{l_node}"
180
+
181
+ l_node
182
+ end
183
+
184
+ ##
185
+ # Get all registered nodes.
186
+ #
187
+ # @return [Array<Hash{ip=>String, group=>String, user=>String}>]<Paste>
188
+ def nodes
189
+ @@nodes
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ include Assemblr::Remote
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'assemblr'
4
+
5
+ require 'open3'
6
+
7
+ module Assemblr
8
+ ##
9
+ # Defines methods for common shell commands.
10
+ module Shell
11
+ def self.included(_)
12
+ expose_method :exec
13
+ end
14
+
15
+ class << self
16
+ # Execute a command locally.
17
+ #
18
+ # @param cmd [String] the command to run locally
19
+ # @param inject [Array<String>] strings to inject into stdin
20
+ #
21
+ # @return [Array(String, Process::Status)]
22
+ def exec(cmd, inject: [])
23
+ log_string = "running `#{cmd}` locally"
24
+ log_string += " with these inputs: #{inject}" unless inject.empty?
25
+ log_info log_string
26
+
27
+ result = ''
28
+ status = nil
29
+ Open3.popen2e(cmd) do |i, o, wait|
30
+ inject.each do |line|
31
+ line = line.strip
32
+ i.write line + "\n"
33
+ end
34
+ i.close
35
+ result = o.read
36
+ status = wait.value
37
+ end
38
+
39
+ if status.exitstatus != 0
40
+ code = status.exitstatus
41
+ log_error "local command `#{cmd}` exited with a status of #{code}"
42
+ else
43
+ log_success "local command `#{cmd}` executed successfully"
44
+ end
45
+
46
+ [result, status]
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ include Assemblr::Shell
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Assemblr
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assemblr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Burmeister-Morrison
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-08 00:00:00.000000000 Z
11
+ date: 2020-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -110,8 +110,9 @@ files:
110
110
  - bin/console
111
111
  - bin/setup
112
112
  - lib/assemblr.rb
113
- - lib/assemblr/logging.rb
114
- - lib/assemblr/utilities.rb
113
+ - lib/assemblr/network.rb
114
+ - lib/assemblr/remote.rb
115
+ - lib/assemblr/shell.rb
115
116
  - lib/assemblr/version.rb
116
117
  homepage: https://github.com/rburmorrison/assemblr
117
118
  licenses:
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tty-logger'
4
-
5
- module Assemblr
6
- Logger = TTY::Logger.new do |config|
7
- config.metadata = %i[time date]
8
- end
9
-
10
- m_log = true
11
- m_quit_on_error = true
12
-
13
- ##
14
- # @!method info(message)
15
- # Logs an info message.
16
- #
17
- # @return [void]
18
-
19
- ##
20
- # @!method success(message)
21
- # Logs a success message.
22
- #
23
- # @return [void]
24
-
25
- ##
26
- # @!method warn(message)
27
- # Logs a warning message.
28
- #
29
- # @return [void]
30
- %i[info success warn].each do |level|
31
- define_method level do |*args, &block|
32
- return unless m_log
33
-
34
- Logger.send(level, *args, &block)
35
- end
36
- end
37
-
38
- ##
39
- # @!method error(message)
40
- # Logs a error message. If configured, this will end the task with error code
41
- # 1.
42
- #
43
- # @return [void]
44
- define_method :error do |*args, &block|
45
- Logger.send(:error, *args, &block) if m_log
46
- exit 1 if m_quit_on_error
47
- end
48
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
- require 'net/ping'
5
-
6
- ##
7
- # A collection of utility functions for assemblr.
8
- module Assemblr
9
- ##
10
- # Retrieve the first ip address that is not '127.0.0.1' on the local machine.
11
- #
12
- # @param pattern [Regexp] a pattern to test the ips against
13
- def get_local_ip(pattern = //)
14
- Socket.ip_address_list.each do |ip|
15
- address = ip.ip_address
16
- next if address == '127.0.0.1'
17
- return address if address.match?(pattern)
18
- end
19
- ''
20
- end
21
-
22
- ##
23
- # Check if a remote machine can be contacted.
24
- #
25
- # @return [Bool]
26
- def reachable?(ip)
27
- external = Net::Ping::External.new(ip)
28
-
29
- info %(attempting to contact host "#{ip}"...)
30
- reachable = external.ping || external.ping6
31
- if reachable
32
- success %(host "#{ip}" is reachable)
33
- else
34
- error %(unable to contact host "#{ip}")
35
- end
36
-
37
- reachable
38
- end
39
- end