kanrisuru 0.1.0 → 0.2.1

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -0
  3. data/.rubocop.yml +47 -0
  4. data/.rubocop_todo.yml +0 -0
  5. data/CHANGELOG.md +0 -0
  6. data/Gemfile +2 -5
  7. data/LICENSE.txt +1 -1
  8. data/README.md +143 -7
  9. data/Rakefile +5 -3
  10. data/bin/console +4 -3
  11. data/kanrisuru.gemspec +21 -12
  12. data/lib/kanrisuru.rb +41 -2
  13. data/lib/kanrisuru/command.rb +99 -0
  14. data/lib/kanrisuru/core.rb +53 -0
  15. data/lib/kanrisuru/core/archive.rb +154 -0
  16. data/lib/kanrisuru/core/disk.rb +302 -0
  17. data/lib/kanrisuru/core/file.rb +332 -0
  18. data/lib/kanrisuru/core/find.rb +108 -0
  19. data/lib/kanrisuru/core/group.rb +97 -0
  20. data/lib/kanrisuru/core/ip.rb +1032 -0
  21. data/lib/kanrisuru/core/mount.rb +138 -0
  22. data/lib/kanrisuru/core/path.rb +140 -0
  23. data/lib/kanrisuru/core/socket.rb +168 -0
  24. data/lib/kanrisuru/core/stat.rb +104 -0
  25. data/lib/kanrisuru/core/stream.rb +121 -0
  26. data/lib/kanrisuru/core/system.rb +348 -0
  27. data/lib/kanrisuru/core/transfer.rb +203 -0
  28. data/lib/kanrisuru/core/user.rb +198 -0
  29. data/lib/kanrisuru/logger.rb +8 -0
  30. data/lib/kanrisuru/mode.rb +277 -0
  31. data/lib/kanrisuru/os_package.rb +235 -0
  32. data/lib/kanrisuru/remote.rb +10 -0
  33. data/lib/kanrisuru/remote/cluster.rb +95 -0
  34. data/lib/kanrisuru/remote/cpu.rb +68 -0
  35. data/lib/kanrisuru/remote/env.rb +33 -0
  36. data/lib/kanrisuru/remote/file.rb +354 -0
  37. data/lib/kanrisuru/remote/fstab.rb +412 -0
  38. data/lib/kanrisuru/remote/host.rb +191 -0
  39. data/lib/kanrisuru/remote/memory.rb +19 -0
  40. data/lib/kanrisuru/remote/os.rb +87 -0
  41. data/lib/kanrisuru/result.rb +78 -0
  42. data/lib/kanrisuru/template.rb +32 -0
  43. data/lib/kanrisuru/util.rb +40 -0
  44. data/lib/kanrisuru/util/bits.rb +203 -0
  45. data/lib/kanrisuru/util/fs_mount_opts.rb +655 -0
  46. data/lib/kanrisuru/util/os_family.rb +213 -0
  47. data/lib/kanrisuru/util/signal.rb +161 -0
  48. data/lib/kanrisuru/version.rb +3 -1
  49. data/spec/functional/core/archive_spec.rb +228 -0
  50. data/spec/functional/core/disk_spec.rb +80 -0
  51. data/spec/functional/core/file_spec.rb +341 -0
  52. data/spec/functional/core/find_spec.rb +52 -0
  53. data/spec/functional/core/group_spec.rb +65 -0
  54. data/spec/functional/core/ip_spec.rb +71 -0
  55. data/spec/functional/core/path_spec.rb +93 -0
  56. data/spec/functional/core/socket_spec.rb +31 -0
  57. data/spec/functional/core/stat_spec.rb +98 -0
  58. data/spec/functional/core/stream_spec.rb +99 -0
  59. data/spec/functional/core/system_spec.rb +96 -0
  60. data/spec/functional/core/transfer_spec.rb +108 -0
  61. data/spec/functional/core/user_spec.rb +76 -0
  62. data/spec/functional/os_package_spec.rb +75 -0
  63. data/spec/functional/remote/cluster_spec.rb +45 -0
  64. data/spec/functional/remote/cpu_spec.rb +41 -0
  65. data/spec/functional/remote/env_spec.rb +36 -0
  66. data/spec/functional/remote/fstab_spec.rb +76 -0
  67. data/spec/functional/remote/host_spec.rb +68 -0
  68. data/spec/functional/remote/memory_spec.rb +29 -0
  69. data/spec/functional/remote/os_spec.rb +63 -0
  70. data/spec/functional/remote/remote_file_spec.rb +180 -0
  71. data/spec/helper/test_hosts.rb +68 -0
  72. data/spec/hosts.json +92 -0
  73. data/spec/spec_helper.rb +11 -3
  74. data/spec/unit/fstab_spec.rb +22 -0
  75. data/spec/unit/kanrisuru_spec.rb +9 -0
  76. data/spec/unit/mode_spec.rb +183 -0
  77. data/spec/unit/template_spec.rb +13 -0
  78. data/spec/unit/util_spec.rb +177 -0
  79. data/spec/zz_reboot_spec.rb +46 -0
  80. metadata +136 -13
  81. data/spec/kanrisuru_spec.rb +0 -9
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kanrisuru
4
+ module Core
5
+ module Stream
6
+ extend OsPackage::Define
7
+
8
+ os_define :linux, :head
9
+ os_define :linux, :tail
10
+ os_define :linux, :read_file_chunk
11
+ os_define :linux, :sed
12
+ os_define :linux, :echo
13
+ os_define :linux, :cat
14
+
15
+ def head(path, opts = {})
16
+ command = Kanrisuru::Command.new('head')
17
+ command.append_arg('-c', opts[:bytes])
18
+ command.append_arg('-n', opts[:lines])
19
+ command << path
20
+
21
+ execute_shell(command)
22
+
23
+ Kanrisuru::Result.new(command, &:to_a)
24
+ end
25
+
26
+ def tail(path, opts = {})
27
+ command = Kanrisuru::Command.new('tail')
28
+ command.append_arg('-c', opts[:bytes])
29
+ command.append_arg('-n', opts[:lines])
30
+ command << path
31
+
32
+ execute_shell(command)
33
+
34
+ Kanrisuru::Result.new(command, &:to_a)
35
+ end
36
+
37
+ def read_file_chunk(path, start_line, end_line)
38
+ if start_line.instance_of?(Integer) || end_line.instance_of?(Integer) ||
39
+ start_line > end_line || start_line.negative?
40
+ raise ArgumentError, 'Invalid start or end'
41
+ end
42
+
43
+ end_line = end_line - start_line + 1
44
+ command = Kanrisuru::Command.new("tail -n +#{start_line} #{path} | head -n #{end_line}")
45
+
46
+ execute_shell(command)
47
+
48
+ Kanrisuru::Result.new(command, &:to_a)
49
+ end
50
+
51
+ def sed(path, expression, replacement, opts = {})
52
+ command = Kanrisuru::Command.new('sed')
53
+ command.append_flag('-i', opts[:in_place])
54
+ command.append_flag('-r', opts[:regexp_extended])
55
+
56
+ command << "'s/#{expression}/#{replacement}/g'"
57
+ command << "'#{path}'"
58
+
59
+ append_file(command, opts)
60
+ execute_shell(command)
61
+
62
+ Kanrisuru::Result.new(command) do |cmd|
63
+ if Kanrisuru::Util.present?(opts[:new_file])
64
+ nil
65
+ else
66
+ cmd.to_a.join("\n")
67
+ end
68
+ end
69
+ end
70
+
71
+ def echo(text, opts = {})
72
+ command = Kanrisuru::Command.new('echo')
73
+ command.append_flag('-e', opts[:backslash])
74
+ command << "'#{text}'"
75
+
76
+ append_file(command, opts)
77
+ execute_shell(command)
78
+
79
+ Kanrisuru::Result.new(command) do |cmd|
80
+ if Kanrisuru::Util.present?(opts[:new_file])
81
+ nil
82
+ else
83
+ cmd.to_s
84
+ end
85
+ end
86
+ end
87
+
88
+ def cat(files, opts = {})
89
+ command = Kanrisuru::Command.new('cat')
90
+ command.append_flag('-T', opts[:show_tabs])
91
+ command.append_flag('-n', opts[:number])
92
+ command.append_flag('-s', opts[:squeeze_blank])
93
+ command.append_flag('-v', opts[:show_nonprinting])
94
+ command.append_flag('-E', opts[:show_ends])
95
+ command.append_flag('-b', opts[:number_nonblank])
96
+ command.append_flag('-A', opts[:show_all])
97
+
98
+ files = files.instance_of?(String) ? [files] : files
99
+ command << files.join(' ')
100
+
101
+ append_file(command, opts)
102
+ execute_shell(command)
103
+
104
+ Kanrisuru::Result.new(command, &:to_a)
105
+ end
106
+
107
+ private
108
+
109
+ def append_file(command, opts)
110
+ return unless Kanrisuru::Util.present?(opts[:new_file])
111
+
112
+ case opts[:mode]
113
+ when 'append'
114
+ command << ">> #{opts[:new_file]}"
115
+ when 'write'
116
+ command << "> #{opts[:new_file]}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,348 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module Kanrisuru
6
+ module Core
7
+ module System
8
+ extend OsPackage::Define
9
+
10
+ os_define :linux, :load_env
11
+ os_define :linux, :cpu_info
12
+ os_define :linux, :load_average
13
+ os_define :linux, :free
14
+ os_define :linux, :ps
15
+ os_define :linux, :kill
16
+
17
+ os_define :linux, :uptime
18
+ os_define :linux, :w
19
+ os_define :linux, :who
20
+
21
+ os_define :linux, :reboot
22
+ os_define :linux, :poweroff
23
+
24
+ ProcessInfo = Struct.new(
25
+ :uid, :user, :gid, :group, :ppid, :pid,
26
+ :cpu_usage, :memory_usage, :stat, :priority,
27
+ :flags, :policy_abbr, :policy, :cpu_time, :command
28
+ )
29
+
30
+ Uptime = Struct.new(
31
+ :boot_time, :uptime, :seconds, :minutes, :hours, :days
32
+ )
33
+
34
+ UserLoggedIn = Struct.new(:user, :tty, :ip, :login, :idle, :jcpu, :pcpu, :command)
35
+
36
+ def load_env
37
+ command = Kanrisuru::Command.new('env')
38
+ execute_shell(command)
39
+
40
+ Kanrisuru::Result.new(command) do |cmd|
41
+ string = cmd.to_s
42
+ hash = {}
43
+
44
+ rows = string.split("\n")
45
+ rows.each do |row|
46
+ key, value = row.split('=', 2)
47
+ hash[key] = value
48
+ end
49
+
50
+ hash
51
+ end
52
+ end
53
+
54
+ def cpu_info(spec)
55
+ name =
56
+ case spec
57
+ when 'sockets'
58
+ '^Socket'
59
+ when 'cores_per_socket'
60
+ '^Core'
61
+ when 'threads'
62
+ '^Thread'
63
+ when 'cores'
64
+ '^CPU('
65
+ else
66
+ return
67
+ end
68
+
69
+ command = Kanrisuru::Command.new("lscpu | grep -i '#{name}' | awk '{print $NF}'")
70
+ execute(command)
71
+
72
+ Kanrisuru::Result.new(command, &:to_i)
73
+ end
74
+
75
+ def load_average
76
+ command = Kanrisuru::Command.new("cat /proc/loadavg | awk '{print $1,$2,$3}'")
77
+ execute(command)
78
+
79
+ Kanrisuru::Result.new(command) do |cmd|
80
+ cmd.to_s.split.map(&:to_f)
81
+ end
82
+ end
83
+
84
+ def free(type)
85
+ conversions = {
86
+ total: 'MemTotal',
87
+ free: 'MemFree',
88
+ swap: 'SwapTotal',
89
+ swap_free: 'SwapFree'
90
+ }
91
+
92
+ option = conversions[type.to_sym]
93
+ raise ArgumentError, 'Invalid mem type' unless option
94
+
95
+ command = Kanrisuru::Command.new("cat /proc/meminfo | grep -i '^#{option}' | awk '{print $2}'")
96
+ execute(command)
97
+
98
+ ## In kB
99
+ Kanrisuru::Result.new(command, &:to_i)
100
+ end
101
+
102
+ def ps(opts = {})
103
+ group = opts[:group]
104
+ user = opts[:user]
105
+ pid = opts[:pid]
106
+ ppid = opts[:ppid]
107
+ # tree = opts[:tree]
108
+
109
+ command = Kanrisuru::Command.new('ps ww')
110
+
111
+ if !user.nil? || !group.nil? || !pid.nil? || !ppid.nil?
112
+ command.append_arg('--user', user.instance_of?(Array) ? user.join(',') : user)
113
+ command.append_arg('--group', group.instance_of?(Array) ? group.join(',') : group)
114
+ command.append_arg('--pid', pid.instance_of?(Array) ? pid.join(',') : pid)
115
+ command.append_arg('--ppid', ppid.instance_of?(Array) ? ppid.join(',') : ppid)
116
+ else
117
+ command.append_flag('ax')
118
+ end
119
+
120
+ command.append_arg('-o', 'uid,user,gid,group,ppid,pid,pcpu,pmem,stat,pri,flags,policy,time,cmd')
121
+ command.append_flag('--no-headers')
122
+
123
+ execute(command)
124
+
125
+ ## Have found issues with regular newline parsing from command
126
+ ## most likely a buffer overflow from SSH buffer.
127
+ ## Join string then split by newline.
128
+ Kanrisuru::Result.new(command) do |cmd|
129
+ result_string = cmd.raw_result.join
130
+ rows = result_string.split("\n")
131
+
132
+ build_process_list(rows)
133
+ end
134
+ end
135
+
136
+ def kill(signal, pids)
137
+ raise ArgumentError, 'Invalid signal' unless Kanrisuru::Util::Signal.valid?(signal)
138
+
139
+ ## Use named signals for readabilitiy
140
+ signal = Kanrisuru::Util::Signal[signal] if signal.instance_of?(Integer)
141
+
142
+ command = Kanrisuru::Command.new('kill')
143
+ command << "-#{signal}"
144
+ command << (pids.instance_of?(Array) ? pids.join(' ') : pids)
145
+
146
+ execute_shell(command)
147
+
148
+ Kanrisuru::Result.new(command)
149
+ end
150
+
151
+ def uptime
152
+ ## Ported from https://github.com/djberg96/sys-uptime
153
+ command = Kanrisuru::Command.new('cat /proc/uptime')
154
+
155
+ execute_shell(command)
156
+
157
+ Kanrisuru::Result.new(command) do |cmd|
158
+ seconds = cmd.to_s.split[0].to_i
159
+ minutes = seconds / 60
160
+ hours = seconds / 3600
161
+ days = seconds / 86_400
162
+
163
+ seconds_dur = seconds
164
+ days_dur = seconds_dur / 86_400
165
+ seconds_dur -= days_dur * 86_400
166
+ hours_dur = seconds_dur / 3600
167
+ seconds_dur -= hours_dur * 3600
168
+ minutes_dur = seconds_dur / 60
169
+ seconds_dur -= minutes_dur * 60
170
+ uptime_s = "#{days_dur}:#{hours_dur}:#{minutes_dur}:#{seconds_dur}"
171
+
172
+ boot_time = Time.now - seconds
173
+
174
+ Uptime.new(
175
+ boot_time,
176
+ uptime_s,
177
+ seconds,
178
+ minutes,
179
+ hours,
180
+ days
181
+ )
182
+ end
183
+ end
184
+
185
+ def w(opts = {})
186
+ users = opts[:users]
187
+ command = Kanrisuru::Command.new('w -h')
188
+
189
+ command << users if Kanrisuru::Util.present?(users)
190
+
191
+ execute(command)
192
+
193
+ Kanrisuru::Result.new(command) do |cmd|
194
+ result_string = cmd.raw_result.join
195
+ rows = result_string.split("\n")
196
+
197
+ rows.map do |row|
198
+ values = *row.split(/\s+/, 8)
199
+ UserLoggedIn.new(
200
+ values[0],
201
+ values[1],
202
+ IPAddr.new(values[2]),
203
+ values[3],
204
+ values[4],
205
+ values[5].to_f,
206
+ values[6].to_f,
207
+ values[7]
208
+ )
209
+ end
210
+ end
211
+ end
212
+
213
+ def who(opts = {})
214
+ w(opts)
215
+ end
216
+
217
+ def reboot(opts = {})
218
+ time = opts[:time]
219
+ cancel = opts[:cancel]
220
+ message = opts[:message]
221
+ no_wall = opts[:no_wall]
222
+
223
+ command = Kanrisuru::Command.new('shutdown')
224
+
225
+ if Kanrisuru::Util.present?(cancel)
226
+ command.append_flag('-c')
227
+ command << message if message
228
+ else
229
+ command.append_flag('-r')
230
+
231
+ time = format_shutdown_time(time) if Kanrisuru::Util.present?(time)
232
+
233
+ if Kanrisuru::Util.present?(no_wall)
234
+ command.append_flag('--no-wall')
235
+ else
236
+ command << time if time
237
+ command << message if message
238
+ end
239
+ end
240
+
241
+ begin
242
+ execute_shell(command)
243
+ Kanrisuru::Result.new(command)
244
+ rescue IOError
245
+ ## When rebooting 'now', ssh io stream closes
246
+ ## Set exit status to 0
247
+ command.handle_status(0)
248
+ Kanrisuru::Result.new(command)
249
+ end
250
+ end
251
+
252
+ def poweroff(opts = {})
253
+ time = opts[:time]
254
+ cancel = opts[:cancel]
255
+ message = opts[:message]
256
+ no_wall = opts[:no_wall]
257
+
258
+ command = Kanrisuru::Command.new('shutdown')
259
+
260
+ if Kanrisuru::Util.present?(cancel)
261
+ command.append_flag('-c')
262
+ command << message if message
263
+ else
264
+ time = format_shutdown_time(time) if Kanrisuru::Util.present?(time)
265
+
266
+ if Kanrisuru::Util.present?(no_wall)
267
+ command.append_flag('--no-wall')
268
+ else
269
+ command << time if time
270
+ command << message if message
271
+ end
272
+ end
273
+
274
+ begin
275
+ execute_shell(command)
276
+
277
+ Kanrisuru::Result.new(command)
278
+ rescue IOError
279
+ ## When powering off 'now', ssh io stream closes
280
+ ## Set exit status to 0
281
+ command.handle_status(0)
282
+
283
+ Kanrisuru::Result.new(command)
284
+ end
285
+ end
286
+
287
+ private
288
+
289
+ def parse_policy_abbr(value)
290
+ case value
291
+ when '-'
292
+ 'not reported'
293
+ when 'TS'
294
+ 'SCHED_OTHER'
295
+ when 'FF'
296
+ 'SCHED_FIFO'
297
+ when 'RR'
298
+ 'SCHED_RR'
299
+ when 'B'
300
+ 'SCHED_BATCH'
301
+ when 'ISO'
302
+ 'SCHED_ISO'
303
+ when 'IDL'
304
+ 'SCHED_IDLE'
305
+ when 'DLN'
306
+ 'SCHED_DEADLINE'
307
+ else
308
+ 'unknown value'
309
+ end
310
+ end
311
+
312
+ def build_process_list(rows)
313
+ rows.map do |row|
314
+ values = *row.split(/\s+/, 15)
315
+ values.shift if values[0] == ''
316
+
317
+ ProcessInfo.new(
318
+ values[0].to_i,
319
+ values[1],
320
+ values[2].to_i,
321
+ values[3],
322
+ values[4].to_i,
323
+ values[5].to_i,
324
+ values[6].to_f,
325
+ values[7].to_f,
326
+ values[8],
327
+ values[9].to_i,
328
+ values[10].to_i,
329
+ values[11],
330
+ parse_policy_abbr(values[11]),
331
+ values[12],
332
+ values[13]
333
+ )
334
+ end
335
+ end
336
+
337
+ def format_shutdown_time(time)
338
+ if time.instance_of?(Integer) || !/^[0-9]+$/.match(time).nil?
339
+ "+#{time}"
340
+ elsif !/^[0-9]{0,2}:[0-9]{0,2}$/.match(time).nil? || time == 'now'
341
+ time
342
+ else
343
+ raise ArgumentError, 'Invalid time format'
344
+ end
345
+ end
346
+ end
347
+ end
348
+ end