r-train 0.9.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 (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