r-train 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +45 -0
  4. data/.travis.yml +12 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE +201 -0
  7. data/README.md +137 -0
  8. data/Rakefile +39 -0
  9. data/lib/train.rb +100 -0
  10. data/lib/train/errors.rb +23 -0
  11. data/lib/train/extras.rb +15 -0
  12. data/lib/train/extras/command_wrapper.rb +105 -0
  13. data/lib/train/extras/file_common.rb +131 -0
  14. data/lib/train/extras/linux_file.rb +74 -0
  15. data/lib/train/extras/linux_lsb.rb +60 -0
  16. data/lib/train/extras/os_common.rb +131 -0
  17. data/lib/train/extras/os_detect_darwin.rb +32 -0
  18. data/lib/train/extras/os_detect_linux.rb +126 -0
  19. data/lib/train/extras/os_detect_unix.rb +77 -0
  20. data/lib/train/extras/os_detect_windows.rb +73 -0
  21. data/lib/train/extras/stat.rb +92 -0
  22. data/lib/train/extras/windows_file.rb +85 -0
  23. data/lib/train/options.rb +80 -0
  24. data/lib/train/plugins.rb +40 -0
  25. data/lib/train/plugins/base_connection.rb +86 -0
  26. data/lib/train/plugins/transport.rb +49 -0
  27. data/lib/train/transports/docker.rb +102 -0
  28. data/lib/train/transports/local.rb +52 -0
  29. data/lib/train/transports/local_file.rb +77 -0
  30. data/lib/train/transports/local_os.rb +51 -0
  31. data/lib/train/transports/mock.rb +125 -0
  32. data/lib/train/transports/ssh.rb +163 -0
  33. data/lib/train/transports/ssh_connection.rb +216 -0
  34. data/lib/train/transports/winrm.rb +187 -0
  35. data/lib/train/transports/winrm_connection.rb +258 -0
  36. data/lib/train/version.rb +7 -0
  37. data/test/integration/.kitchen.yml +43 -0
  38. data/test/integration/Berksfile +3 -0
  39. data/test/integration/bootstrap.sh +17 -0
  40. data/test/integration/chefignore +1 -0
  41. data/test/integration/cookbooks/test/metadata.rb +1 -0
  42. data/test/integration/cookbooks/test/recipes/default.rb +101 -0
  43. data/test/integration/docker_run.rb +153 -0
  44. data/test/integration/docker_test.rb +24 -0
  45. data/test/integration/docker_test_container.rb +24 -0
  46. data/test/integration/helper.rb +58 -0
  47. data/test/integration/sudo/nopasswd.rb +16 -0
  48. data/test/integration/sudo/passwd.rb +21 -0
  49. data/test/integration/sudo/run_as.rb +12 -0
  50. data/test/integration/test-runner.yaml +24 -0
  51. data/test/integration/test_local.rb +19 -0
  52. data/test/integration/test_ssh.rb +24 -0
  53. data/test/integration/tests/path_block_device_test.rb +74 -0
  54. data/test/integration/tests/path_character_device_test.rb +74 -0
  55. data/test/integration/tests/path_file_test.rb +79 -0
  56. data/test/integration/tests/path_folder_test.rb +88 -0
  57. data/test/integration/tests/path_missing_test.rb +77 -0
  58. data/test/integration/tests/path_pipe_test.rb +78 -0
  59. data/test/integration/tests/path_symlink_test.rb +83 -0
  60. data/test/integration/tests/run_command_test.rb +28 -0
  61. data/test/unit/extras/command_wrapper_test.rb +41 -0
  62. data/test/unit/extras/file_common_test.rb +133 -0
  63. data/test/unit/extras/linux_file_test.rb +98 -0
  64. data/test/unit/extras/os_common_test.rb +258 -0
  65. data/test/unit/extras/stat_test.rb +105 -0
  66. data/test/unit/helper.rb +6 -0
  67. data/test/unit/plugins/connection_test.rb +44 -0
  68. data/test/unit/plugins/transport_test.rb +111 -0
  69. data/test/unit/plugins_test.rb +22 -0
  70. data/test/unit/train_test.rb +132 -0
  71. data/test/unit/transports/local_file_test.rb +112 -0
  72. data/test/unit/transports/local_test.rb +73 -0
  73. data/test/unit/transports/mock_test.rb +76 -0
  74. data/test/unit/transports/ssh_test.rb +95 -0
  75. data/test/unit/version_test.rb +8 -0
  76. data/train.gemspec +32 -0
  77. metadata +299 -0
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ #
5
+ # This is heavily based on:
6
+ #
7
+ # OHAI https://github.com/chef/ohai
8
+ # by Adam Jacob, Chef Software Inc
9
+ #
10
+
11
+ module Train::Extras
12
+ module LinuxLSB
13
+ def lsb_config(content)
14
+ {
15
+ id: content[/^DISTRIB_ID=["']?(.+?)["']?$/, 1],
16
+ release: content[/^DISTRIB_RELEASE=["']?(.+?)["']?$/, 1],
17
+ codename: content[/^DISTRIB_CODENAME=["']?(.+?)["']?$/, 1],
18
+ }
19
+ end
20
+
21
+ def lsb_release
22
+ raw = @backend.run_command('lsb_release -a').stdout
23
+ {
24
+ id: raw[/^Distributor ID:\s+(.+)$/, 1],
25
+ release: raw[/^Release:\s+(.+)$/, 1],
26
+ codename: raw[/^Codename:\s+(.+)$/, 1],
27
+ }
28
+ end
29
+
30
+ def lsb
31
+ return @lsb if defined?(@lsb)
32
+ @lsb = {}
33
+ if !(raw = get_config('/etc/lsb-release')).nil?
34
+ @lsb = lsb_config(raw)
35
+ elsif unix_file?('/usr/bin/lsb_release')
36
+ @lsb = lsb_release
37
+ end
38
+ @lsb
39
+ end
40
+
41
+ def detect_linux_via_lsb
42
+ return false if lsb[:id].nil?
43
+ id = lsb[:id].downcase
44
+ case id
45
+ when /redhat/
46
+ @platform[:family] = 'redhat'
47
+ when /amazon/
48
+ @platform[:family] = 'amazon'
49
+ when /scientificsl/
50
+ @platform[:family] = 'scientific'
51
+ when /xenserver/
52
+ @platform[:family] = 'xenserver'
53
+ else
54
+ @platform[:family] = id
55
+ end
56
+ @platform[:release] = lsb[:release]
57
+ true
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,131 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ #
5
+ # This is heavily based on:
6
+ #
7
+ # OHAI https://github.com/chef/ohai
8
+ # by Adam Jacob, Chef Software Inc
9
+ #
10
+
11
+ require 'train/extras/os_detect_darwin'
12
+ require 'train/extras/os_detect_linux'
13
+ require 'train/extras/os_detect_unix'
14
+ require 'train/extras/os_detect_windows'
15
+
16
+ module Train::Extras
17
+ class OSCommon
18
+ include Train::Extras::DetectDarwin
19
+ include Train::Extras::DetectLinux
20
+ include Train::Extras::DetectUnix
21
+ include Train::Extras::DetectWindows
22
+
23
+ def initialize(backend, platform = nil)
24
+ @backend = backend
25
+ @platform = platform || {}
26
+ detect_family
27
+ end
28
+
29
+ def [](key)
30
+ @platform[key]
31
+ end
32
+
33
+ def to_hash
34
+ @platform
35
+ end
36
+
37
+ OS = {
38
+ 'redhat' => %w{
39
+ redhat oracle centos fedora amazon scientific xenserver
40
+ },
41
+ 'debian' => %w{
42
+ debian ubuntu linuxmint raspbian
43
+ },
44
+ 'suse' => %w{
45
+ suse opensuse
46
+ },
47
+ 'bsd' => %w{
48
+ freebsd netbsd openbsd darwin
49
+ },
50
+ 'solaris' => %w{
51
+ solaris smartos openindiana opensolaris solaris2 nexentacore
52
+ },
53
+ 'windows' => %w{
54
+ windows
55
+ },
56
+ }
57
+
58
+ OS['linux'] = %w{linux alpine arch coreos exherbo gentoo slackware} +
59
+ OS['redhat'] + OS['debian'] + OS['suse']
60
+
61
+ OS['unix'] = %w{unix aix} + OS['linux'] + OS['solaris'] + OS['bsd']
62
+
63
+ # Helper methods to check the OS type
64
+ # Provides methods in the form of: linux?, unix?, solaris? ...
65
+ OS.keys.each do |os_family|
66
+ define_method((os_family + '?').to_sym) do
67
+ OS[os_family].include?(@platform[:family])
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def detect_family
74
+ # if some information is already defined, try to verify it
75
+ # with the remaining detection
76
+ unless @platform[:family].nil?
77
+ # return ok if the preconfigured family yielded a good result
78
+ return true if detect_family_type
79
+ # if not, reset the platform to presets and run the full detection
80
+ # TODO: print an error message in this case, as the instantiating
81
+ # backend is doing something wrong
82
+ @platform = {}
83
+ end
84
+
85
+ # TODO: extend base implementation for detecting the family type
86
+ # to Windows and others
87
+ case uname_s
88
+ when /linux/i
89
+ @platform[:family] = 'linux'
90
+ when /./
91
+ @platform[:family] = 'unix'
92
+ else
93
+ # Don't know what this is
94
+ @platform[:family] = nil
95
+ end
96
+
97
+ # try to detect the platform
98
+ return nil unless @platform[:family].nil?
99
+ detect_family_type
100
+ end
101
+
102
+ def detect_family_type
103
+ pf = @platform[:family]
104
+
105
+ return detect_windows if pf == 'windows'
106
+ return detect_darwin if pf == 'darwin'
107
+
108
+ if %w{freebsd netbsd openbsd aix solaris2}.include?(pf)
109
+ return detect_via_uname
110
+ end
111
+
112
+ # unix based systems combine the above
113
+ return true if pf == 'unix' and detect_darwin
114
+ return true if pf == 'unix' and detect_via_uname
115
+
116
+ # if we arrive here, we most likey have a regular linux
117
+ detect_linux
118
+ end
119
+
120
+ def get_config(path)
121
+ res = @backend.run_command("test -f #{path} && cat #{path}")
122
+ # ignore files that can't be read
123
+ return nil if res.exit_status != 0
124
+ res.stdout
125
+ end
126
+
127
+ def unix_file?(path)
128
+ @backend.run_command("test -f #{path}").exit_status == 0
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ #
5
+ # This is heavily based on:
6
+ #
7
+ # OHAI https://github.com/chef/ohai
8
+ # by Adam Jacob, Chef Software Inc
9
+ #
10
+
11
+ module Train::Extras
12
+ module DetectDarwin
13
+ def detect_darwin
14
+ cmd = @backend.run_command('/usr/bin/sw_vers')
15
+ # TODO: print an error in this step of the detection,
16
+ # as it shouldnt happen
17
+ return false if cmd.exit_status != 0
18
+ # TODO: ditto on error
19
+ return false if cmd.stdout.empty?
20
+
21
+ name = cmd.stdout[/^ProductName:\s+(.+)$/, 1]
22
+ # TODO: ditto on error
23
+ return false if name.nil?
24
+ @platform[:name] = name.downcase.chomp.tr(' ', '_')
25
+ @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1]
26
+ @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1]
27
+ # TODO: keep for now due to backwards compatibility with serverspec
28
+ @platform[:family] = 'darwin'
29
+ true
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,126 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ #
5
+ # This is heavily based on:
6
+ #
7
+ # OHAI https://github.com/chef/ohai
8
+ # by Adam Jacob, Chef Software Inc
9
+ #
10
+
11
+ require 'train/extras/linux_lsb'
12
+
13
+ module Train::Extras
14
+ module DetectLinux
15
+ include Train::Extras::LinuxLSB
16
+
17
+ def detect_linux_via_config # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
18
+ if !(raw = get_config('oracle-release')).nil?
19
+ @platform[:family] = 'oracle'
20
+ @platform[:release] = redhatish_version(raw)
21
+ elsif !(raw = get_config('/etc/enterprise-release')).nil?
22
+ @platform[:family] = 'oracle'
23
+ @platform[:release] = redhatish_version(raw)
24
+ elsif !(_raw = get_config('/etc/debian_version')).nil?
25
+ case lsb[:id]
26
+ when /ubuntu/i
27
+ @platform[:family] = 'ubuntu'
28
+ @platform[:release] = lsb[:release]
29
+ when /linuxmint/i
30
+ @platform[:family] = 'linuxmint'
31
+ @platform[:release] = lsb[:release]
32
+ else
33
+ @platform[:family] = 'debian'
34
+ @platform[:family] = 'raspbian' if unix_file?('/usr/bin/raspi-config')
35
+ unless (rel = get_config('/etc/debian_version')).nil?
36
+ @platform[:release] = rel.chomp
37
+ end
38
+ end
39
+ elsif !(raw = get_config('/etc/parallels-release')).nil?
40
+ @platform[:family] = redhatish_platform(raw)
41
+ @platform[:release] = raw[/(\d\.\d\.\d)/, 1]
42
+ elsif !(raw = get_config('/etc/redhat-release')).nil?
43
+ # TODO: Cisco
44
+ # TODO: fully investigate os-release and integrate it;
45
+ # here we just use it for centos
46
+ if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i
47
+ @platform[:family] = 'centos'
48
+ else
49
+ @platform[:family] = redhatish_platform(raw)
50
+ end
51
+ @platform[:release] = redhatish_version(raw)
52
+ elsif !(raw = get_config('/etc/system-release')).nil?
53
+ # Amazon Linux
54
+ @platform[:family] = redhatish_platform(raw)
55
+ @platform[:release] = redhatish_version(raw)
56
+ elsif !(suse = get_config('/etc/SuSE-release')).nil?
57
+ version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.')
58
+ version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == ''
59
+ @platform[:release] = version
60
+ @platform[:family] = 'suse'
61
+ @platform[:family] = 'opensuse' if suse =~ /^openSUSE/
62
+ elsif !(raw = get_config('/etc/arch-release')).nil?
63
+ @platform[:family] = 'arch'
64
+ # Because this is a rolling release distribution,
65
+ # use the kernel release, ex. 4.1.6-1-ARCH
66
+ @platform[:release] = uname_r
67
+ elsif !(raw = get_config('/etc/slackware-version')).nil?
68
+ @platform[:family] = 'slackware'
69
+ @platform[:release] = raw.scan(/(\d+|\.+)/).join
70
+ elsif !(raw = get_config('/etc/exherbo-release')).nil?
71
+ @platform[:family] = 'exherbo'
72
+ # Because this is a rolling release distribution,
73
+ # use the kernel release, ex. 4.1.6
74
+ @platform[:release] = uname_r
75
+ elsif !(raw = get_config('/etc/gentoo-release')).nil?
76
+ @platform[:family] = 'gentoo'
77
+ @platform[:release] = raw.scan(/(\d+|\.+)/).join
78
+ elsif !(raw = get_config('/etc/alpine-release')).nil?
79
+ @platform[:family] = 'alpine'
80
+ @platform[:release] = raw.strip
81
+ elsif !(raw = get_config('/etc/coreos/update.conf')).nil?
82
+ @platform[:family] = 'coreos'
83
+ meta = lsb_config(raw)
84
+ @platform[:release] = meta[:release]
85
+ else
86
+ # in all other cases we didn't detect it
87
+ return false
88
+ end
89
+ # when we get here the detection returned a result
90
+ true
91
+ end
92
+
93
+ def uname_s
94
+ @uname_s ||= @backend.run_command('uname -s').stdout
95
+ end
96
+
97
+ def uname_r
98
+ @uname_r ||= (
99
+ res = @backend.run_command('uname -r').stdout
100
+ res.strip! unless res.nil?
101
+ res
102
+ )
103
+ end
104
+
105
+ def redhatish_platform(conf)
106
+ conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase
107
+ end
108
+
109
+ def redhatish_version(conf)
110
+ return conf[/((\d+) \(Rawhide\))/i, 1].downcase if conf[/rawhide/i]
111
+ return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i]
112
+ conf[/release ([\d\.]+)/, 1]
113
+ end
114
+
115
+ def detect_linux
116
+ # TODO: print an error in this step of the detection
117
+ return false if uname_s.nil? || uname_s.empty?
118
+ return false if uname_r.nil? || uname_r.empty?
119
+
120
+ return true if detect_linux_via_config
121
+ return true if detect_linux_via_lsb
122
+ # in all other cases we failed the detection
123
+ @platform[:family] = 'unknown'
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ #
5
+ # This is heavily based on:
6
+ #
7
+ # OHAI https://github.com/chef/ohai
8
+ # by Adam Jacob, Chef Software Inc
9
+ #
10
+
11
+ module Train::Extras
12
+ module DetectUnix
13
+ def detect_via_uname # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
14
+ case uname_s.downcase
15
+ when /aix/
16
+ @platform[:family] = 'aix'
17
+ out = @backend.run_command('uname -rvp').stdout
18
+ m = out.match(/(\d+)\s+(\d+)\s+(.*)/)
19
+ unless m.nil?
20
+ @platform[:release] = "#{m[2]}.#{m[1]}"
21
+ @platform[:arch] = m[3].to_s
22
+ end
23
+
24
+ when /freebsd/
25
+ @platform[:family] = 'freebsd'
26
+ @platform[:name] = uname_s.lines[0].chomp
27
+ @platform[:release] = uname_r.lines[0].chomp
28
+
29
+ when /netbsd/
30
+ @platform[:family] = 'netbsd'
31
+ @platform[:name] = uname_s.lines[0].chomp
32
+ @platform[:release] = uname_r.lines[0].chomp
33
+
34
+ when /openbsd/
35
+ @platform[:family] = 'openbsd'
36
+ @platform[:name] = uname_s.lines[0].chomp
37
+ @platform[:release] = uname_r.lines[0].chomp
38
+
39
+ when /sunos/
40
+ @platform[:family] = 'solaris'
41
+ if uname_r =~ /5\.10/
42
+ # TODO: should be string!
43
+ @platform[:release] = 10
44
+ else
45
+ rel = get_config('/etc/release')
46
+ case rel
47
+ when /^.*(SmartOS).*$/
48
+ @platform[:family] = 'smartos'
49
+ when !(m = /^\s*(OmniOS).*r(\d+).*$/).nil?
50
+ @platform[:family] = 'omnios'
51
+ @platform[:release] = m[2]
52
+ when !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/).nil?
53
+ @platform[:family] = 'openindiana'
54
+ @platform[:release] = m[2]
55
+ when /^\s*(OpenSolaris).*snv_(\d+).*$/
56
+ @platform[:family] = 'opensolaris'
57
+ @platform[:release] = m[2]
58
+ when !(m = /Oracle Solaris (\d+)/).nil?
59
+ # TODO: should be string!
60
+ @platform[:release] = m[1].to_i
61
+ @platform[:family] = 'solaris2'
62
+ when /^\s*(Solaris)\s.*$/
63
+ @platform[:family] = 'solaris2'
64
+ when /^\s*(NexentaCore)\s.*$/
65
+ @platform[:family] = 'nexentacore'
66
+ end
67
+ end
68
+
69
+ else
70
+ # in all other cases we didn't detect it
71
+ return false
72
+ end
73
+ # when we get here the detection returned a result
74
+ true
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ #
5
+ # This is heavily based on:
6
+ #
7
+ # OHAI https://github.com/chef/ohai
8
+ # by Adam Jacob, Chef Software Inc
9
+ #
10
+
11
+ require 'json'
12
+
13
+ module Train::Extras
14
+ module DetectWindows
15
+ # See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832%28v=vs.85%29.aspx
16
+ # Product Type:
17
+ # Work Station (1)
18
+ # Domain Controller (2)
19
+ # Server (3)
20
+ WINDOWS_VERSIONS = {
21
+ '0' => '3.1',
22
+ '140' => '95',
23
+ '1410' => '98',
24
+ '1490' => 'ME',
25
+ '1351' => 'NT 3.51',
26
+ '3351' => 'NT 3.51 Server',
27
+ '1240' => 'NT 4.0',
28
+ '3240' => 'NT 4.0 Server',
29
+ '1250' => '2000',
30
+ '1251' => 'XP',
31
+ '3252' => 'Server 2003',
32
+ '1252' => 'Vista',
33
+ '3260' => 'Server 2008',
34
+ '1261' => '7',
35
+ '3261' => 'Server 2008 R2',
36
+ '1262' => '8',
37
+ '3262' => 'Server 2012',
38
+ '1263' => '8.1',
39
+ '3263' => 'Server 2012 R2',
40
+ '12100' => '10',
41
+ '32100' => 'Server 2016',
42
+ }
43
+
44
+ def windows_version(json)
45
+ producttype = json['OS']['ProductType'].to_s
46
+ # do not distigush between domain controller and server
47
+ producttype = '3' if producttype == '2'
48
+ platform = json['OSVersion']['Platform'].to_s
49
+ major = json['OSVersion']['Version']['Major'].to_s
50
+ minor = json['OSVersion']['Version']['Minor'].to_s
51
+ # construct it
52
+ producttype + platform + major + minor
53
+ end
54
+
55
+ def detect_windows
56
+ cmd = 'New-Object -Type PSObject | Add-Member -MemberType NoteProperty '\
57
+ '-Name OS -Value (Get-WmiObject -Class Win32_OperatingSystem) '\
58
+ '-PassThru | Add-Member -MemberType NoteProperty -Name OSVersion '\
59
+ '-Value ([Environment]::OSVersion) -PassThru | ConvertTo-Json'
60
+ res = @backend.run_command(cmd)
61
+ # TODO: error as this shouldnt be happening at this point
62
+ return false if res.exit_status != 0 or res.stdout.empty?
63
+
64
+ json = JSON.parse(res.stdout)
65
+ return false if json.nil? or json.empty?
66
+ version = windows_version(json)
67
+
68
+ @platform[:family] = 'windows'
69
+ @platform[:release] = WINDOWS_VERSIONS[version]
70
+ true
71
+ end
72
+ end
73
+ end