train-core 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +780 -0
  3. data/Gemfile +36 -0
  4. data/LICENSE +201 -0
  5. data/README.md +197 -0
  6. data/lib/train.rb +158 -0
  7. data/lib/train/errors.rb +32 -0
  8. data/lib/train/extras.rb +11 -0
  9. data/lib/train/extras/command_wrapper.rb +137 -0
  10. data/lib/train/extras/stat.rb +132 -0
  11. data/lib/train/file.rb +151 -0
  12. data/lib/train/file/local.rb +75 -0
  13. data/lib/train/file/local/unix.rb +96 -0
  14. data/lib/train/file/local/windows.rb +63 -0
  15. data/lib/train/file/remote.rb +36 -0
  16. data/lib/train/file/remote/aix.rb +21 -0
  17. data/lib/train/file/remote/linux.rb +19 -0
  18. data/lib/train/file/remote/qnx.rb +41 -0
  19. data/lib/train/file/remote/unix.rb +106 -0
  20. data/lib/train/file/remote/windows.rb +94 -0
  21. data/lib/train/options.rb +80 -0
  22. data/lib/train/platforms.rb +84 -0
  23. data/lib/train/platforms/common.rb +34 -0
  24. data/lib/train/platforms/detect.rb +12 -0
  25. data/lib/train/platforms/detect/helpers/os_common.rb +145 -0
  26. data/lib/train/platforms/detect/helpers/os_linux.rb +75 -0
  27. data/lib/train/platforms/detect/helpers/os_windows.rb +120 -0
  28. data/lib/train/platforms/detect/scanner.rb +84 -0
  29. data/lib/train/platforms/detect/specifications/api.rb +15 -0
  30. data/lib/train/platforms/detect/specifications/os.rb +578 -0
  31. data/lib/train/platforms/detect/uuid.rb +34 -0
  32. data/lib/train/platforms/family.rb +26 -0
  33. data/lib/train/platforms/platform.rb +101 -0
  34. data/lib/train/plugins.rb +40 -0
  35. data/lib/train/plugins/base_connection.rb +169 -0
  36. data/lib/train/plugins/transport.rb +49 -0
  37. data/lib/train/transports/local.rb +232 -0
  38. data/lib/train/version.rb +7 -0
  39. data/train-core.gemspec +27 -0
  40. metadata +116 -0
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ module Train
4
+ class File
5
+ class Local < Train::File
6
+ %w{
7
+ exist? file? socket? directory? symlink? pipe? size basename
8
+ }.each do |m|
9
+ define_method m.to_sym do
10
+ ::File.method(m.to_sym).call(@path)
11
+ end
12
+ end
13
+
14
+ def content
15
+ @content ||= ::File.read(@path, encoding: 'UTF-8')
16
+ rescue StandardError => _
17
+ nil
18
+ end
19
+
20
+ def link_path
21
+ return nil unless symlink?
22
+ begin
23
+ @link_path ||= ::File.realpath(@path)
24
+ rescue Errno::ELOOP => _
25
+ # Leave it blank on symbolic loop, same as readlink
26
+ @link_path = ''
27
+ end
28
+ end
29
+
30
+ def block_device?
31
+ ::File.blockdev?(@path)
32
+ end
33
+
34
+ def character_device?
35
+ ::File.chardev?(@path)
36
+ end
37
+
38
+ def type
39
+ case ::File.ftype(@path)
40
+ when 'blockSpecial'
41
+ :block_device
42
+ when 'characterSpecial'
43
+ :character_device
44
+ when 'link'
45
+ :symlink
46
+ when 'fifo'
47
+ :pipe
48
+ else
49
+ ::File.ftype(@path).to_sym
50
+ end
51
+ end
52
+
53
+ %w{
54
+ mode owner group uid gid mtime selinux_label
55
+ }.each do |field|
56
+ define_method field.to_sym do
57
+ stat[field.to_sym]
58
+ end
59
+ end
60
+
61
+ def mode?(sth)
62
+ mode == sth
63
+ end
64
+
65
+ def linked_to?(dst)
66
+ link_path == dst
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # subclass requires are loaded after Train::File::Local is defined
73
+ # to avoid superclass mismatch errors
74
+ require 'train/file/local/unix'
75
+ require 'train/file/local/windows'
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ require 'shellwords'
4
+ require 'train/extras/stat'
5
+
6
+ module Train
7
+ class File
8
+ class Local
9
+ class Unix < Train::File::Local
10
+ def sanitize_filename(path)
11
+ @spath = Shellwords.escape(path) || @path
12
+ end
13
+
14
+ def stat
15
+ return @stat if defined?(@stat)
16
+
17
+ begin
18
+ file_stat =
19
+ if @follow_symlink
20
+ ::File.stat(@path)
21
+ else
22
+ ::File.lstat(@path)
23
+ end
24
+ rescue StandardError => _err
25
+ return @stat = {}
26
+ end
27
+
28
+ @stat = {
29
+ type: Train::Extras::Stat.find_type(file_stat.mode),
30
+ mode: file_stat.mode & 07777,
31
+ mtime: file_stat.mtime.to_i,
32
+ size: file_stat.size,
33
+ owner: pw_username(file_stat.uid),
34
+ uid: file_stat.uid,
35
+ group: pw_groupname(file_stat.gid),
36
+ gid: file_stat.gid,
37
+ }
38
+
39
+ lstat = @follow_symlink ? ' -L' : ''
40
+ res = @backend.run_command("stat#{lstat} #{@spath} 2>/dev/null --printf '%C'")
41
+ if res.exit_status == 0 && !res.stdout.empty? && res.stdout != '?'
42
+ @stat[:selinux_label] = res.stdout.strip
43
+ end
44
+
45
+ @stat
46
+ end
47
+
48
+ def mounted
49
+ @mounted ||=
50
+ @backend.run_command("mount | grep -- ' on #{@spath} '")
51
+ end
52
+
53
+ def grouped_into?(sth)
54
+ group == sth
55
+ end
56
+
57
+ def unix_mode_mask(owner, type)
58
+ o = UNIX_MODE_OWNERS[owner.to_sym]
59
+ return nil if o.nil?
60
+
61
+ t = UNIX_MODE_TYPES[type.to_sym]
62
+ return nil if t.nil?
63
+
64
+ t & o
65
+ end
66
+
67
+ private
68
+
69
+ def pw_username(uid)
70
+ Etc.getpwuid(uid).name
71
+ rescue ArgumentError => _
72
+ nil
73
+ end
74
+
75
+ def pw_groupname(gid)
76
+ Etc.getgrgid(gid).name
77
+ rescue ArgumentError => _
78
+ nil
79
+ end
80
+
81
+ UNIX_MODE_OWNERS = {
82
+ all: 00777,
83
+ owner: 00700,
84
+ group: 00070,
85
+ other: 00007,
86
+ }.freeze
87
+
88
+ UNIX_MODE_TYPES = {
89
+ r: 00444,
90
+ w: 00222,
91
+ x: 00111,
92
+ }.freeze
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ module Train
4
+ class File
5
+ class Local
6
+ class Windows < Train::File::Local
7
+ # Ensures we do not use invalid characters for file names
8
+ # @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#naming_conventions
9
+ def sanitize_filename(path)
10
+ return if path.nil?
11
+ # we do not filter :, backslash and forward slash, since they are part of the path
12
+ @spath = path.gsub(/[<>"|?*]/, '')
13
+ end
14
+
15
+ def product_version
16
+ @product_version ||= @backend.run_command(
17
+ "[System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"#{@spath}\").ProductVersion").stdout.chomp
18
+ end
19
+
20
+ def file_version
21
+ @file_version ||= @backend.run_command(
22
+ "[System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"#{@spath}\").FileVersion").stdout.chomp
23
+ end
24
+
25
+ def owner
26
+ owner = @backend.run_command(
27
+ "Get-Acl \"#{@spath}\" | select -expand Owner").stdout.strip
28
+ return if owner.empty?
29
+ owner
30
+ end
31
+
32
+ def stat
33
+ return @stat if defined?(@stat)
34
+
35
+ begin
36
+ file_stat =
37
+ if @follow_symlink
38
+ ::File.stat(@path)
39
+ else
40
+ ::File.lstat(@path)
41
+ end
42
+ rescue StandardError => _err
43
+ return @stat = {}
44
+ end
45
+
46
+ @stat = {
47
+ type: type,
48
+ mode: file_stat.mode,
49
+ mtime: file_stat.mtime.to_i,
50
+ size: file_stat.size,
51
+ owner: owner,
52
+ uid: file_stat.uid,
53
+ group: nil,
54
+ gid: file_stat.gid,
55
+ selinux_label: nil,
56
+ }
57
+
58
+ @stat
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ module Train
4
+ class File
5
+ class Remote < Train::File
6
+ def basename(suffix = nil, sep = '/')
7
+ fail 'Not yet supported: Suffix in file.basename' unless suffix.nil?
8
+ @basename ||= detect_filename(path, sep || '/')
9
+ end
10
+
11
+ def stat
12
+ return @stat if defined?(@stat)
13
+ @stat = Train::Extras::Stat.stat(@spath, @backend, @follow_symlink)
14
+ end
15
+
16
+ # helper methods provided to any implementing class
17
+ private
18
+
19
+ def detect_filename(path, sep)
20
+ idx = path.rindex(sep)
21
+ return path if idx.nil?
22
+ idx += 1
23
+ return detect_filename(path[0..-2], sep) if idx == path.length
24
+ path[idx..-1]
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ # subclass requires are loaded after Train::File::Remote is defined
31
+ # to avoid superclass mismatch errors
32
+ require 'train/file/remote/aix'
33
+ require 'train/file/remote/linux'
34
+ require 'train/file/remote/qnx'
35
+ require 'train/file/remote/unix'
36
+ require 'train/file/remote/windows'
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require 'train/file/remote/unix'
4
+
5
+ module Train
6
+ class File
7
+ class Remote
8
+ class Aix < Train::File::Remote::Unix
9
+ def link_path
10
+ return nil unless symlink?
11
+ @link_path ||=
12
+ @backend.run_command("perl -e 'print readlink shift' #{@spath}").stdout.chomp
13
+ end
14
+
15
+ def mounted
16
+ @mounted ||= @backend.run_command("lsfs -c #{@spath}")
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'train/file/remote/unix'
4
+
5
+ module Train
6
+ class File
7
+ class Remote
8
+ class Linux < Train::File::Remote::Unix
9
+ def content
10
+ return @content if defined?(@content)
11
+ @content = @backend.run_command("cat #{@spath} || echo -n").stdout
12
+ return @content unless @content.empty?
13
+ @content = nil if directory? or size.nil? or size > 0
14
+ @content
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ #
3
+ # author: Christoph Hartmann
4
+ # author: Dominik Richter
5
+
6
+ require 'train/file/remote/unix'
7
+
8
+ module Train
9
+ class File
10
+ class Remote
11
+ class Qnx < Train::File::Remote::Unix
12
+ def content
13
+ cat = 'cat'
14
+ cat = '/proc/boot/cat' if @backend.os[:release].to_i >= 7
15
+ @content ||= case
16
+ when !exist?
17
+ nil
18
+ else
19
+ @backend.run_command("#{cat} #{@spath}").stdout || ''
20
+ end
21
+ end
22
+
23
+ def type
24
+ if @backend.run_command("file #{@spath}").stdout.include?('directory')
25
+ return :directory
26
+ else
27
+ return :file
28
+ end
29
+ end
30
+
31
+ %w{
32
+ mode owner group uid gid mtime size selinux_label link_path mounted stat
33
+ }.each do |field|
34
+ define_method field.to_sym do
35
+ fail NotImplementedError, "QNX does not implement the #{field}() method yet."
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+
3
+ require 'shellwords'
4
+
5
+ module Train
6
+ class File
7
+ class Remote
8
+ class Unix < Train::File::Remote
9
+ attr_reader :path
10
+
11
+ def sanitize_filename(path)
12
+ @spath = Shellwords.escape(path) || @path
13
+ end
14
+
15
+ def content
16
+ @content ||=
17
+ if !exist? || directory?
18
+ nil
19
+ elsif size.nil? || size.zero?
20
+ ''
21
+ else
22
+ @backend.run_command("cat #{@spath}").stdout || ''
23
+ end
24
+ end
25
+
26
+ def exist?
27
+ @exist ||= (
28
+ f = @follow_symlink ? '' : " || test -L #{@spath}"
29
+ @backend.run_command("test -e #{@spath}"+f)
30
+ .exit_status == 0
31
+ )
32
+ end
33
+
34
+ def mounted
35
+ @mounted ||=
36
+ @backend.run_command("mount | grep -- ' on #{@spath} '")
37
+ end
38
+
39
+ %w{
40
+ type mode owner group uid gid mtime size selinux_label
41
+ }.each do |field|
42
+ define_method field.to_sym do
43
+ stat[field.to_sym]
44
+ end
45
+ end
46
+
47
+ def mode?(sth)
48
+ mode == sth
49
+ end
50
+
51
+ def grouped_into?(sth)
52
+ group == sth
53
+ end
54
+
55
+ def linked_to?(dst)
56
+ link_path == dst
57
+ end
58
+
59
+ def link_path
60
+ symlink? ? path : nil
61
+ end
62
+
63
+ def unix_mode_mask(owner, type)
64
+ o = UNIX_MODE_OWNERS[owner.to_sym]
65
+ return nil if o.nil?
66
+
67
+ t = UNIX_MODE_TYPES[type.to_sym]
68
+ return nil if t.nil?
69
+
70
+ t & o
71
+ end
72
+
73
+ def path
74
+ return @path unless @follow_symlink && symlink?
75
+ @link_path ||= read_target_path
76
+ end
77
+
78
+ private
79
+
80
+ # Returns full path of a symlink target(real dest) or '' on symlink loop
81
+ def read_target_path
82
+ full_path = @backend.run_command("readlink -n #{@spath} -f").stdout
83
+ # Needed for some OSes like OSX that returns relative path
84
+ # when the link and target are in the same directory
85
+ if !full_path.start_with?('/') && full_path != ''
86
+ full_path = ::File.expand_path("../#{full_path}", @spath)
87
+ end
88
+ full_path
89
+ end
90
+
91
+ UNIX_MODE_OWNERS = {
92
+ all: 00777,
93
+ owner: 00700,
94
+ group: 00070,
95
+ other: 00007,
96
+ }.freeze
97
+
98
+ UNIX_MODE_TYPES = {
99
+ r: 00444,
100
+ w: 00222,
101
+ x: 00111,
102
+ }.freeze
103
+ end
104
+ end
105
+ end
106
+ end