train 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +71 -0
  3. data/CHANGELOG.md +308 -0
  4. data/Gemfile +30 -0
  5. data/LICENSE +201 -0
  6. data/README.md +156 -0
  7. data/Rakefile +148 -0
  8. data/lib/train.rb +117 -0
  9. data/lib/train/errors.rb +23 -0
  10. data/lib/train/extras.rb +17 -0
  11. data/lib/train/extras/command_wrapper.rb +148 -0
  12. data/lib/train/extras/file_aix.rb +20 -0
  13. data/lib/train/extras/file_common.rb +161 -0
  14. data/lib/train/extras/file_linux.rb +16 -0
  15. data/lib/train/extras/file_unix.rb +79 -0
  16. data/lib/train/extras/file_windows.rb +91 -0
  17. data/lib/train/extras/linux_lsb.rb +60 -0
  18. data/lib/train/extras/os_common.rb +136 -0
  19. data/lib/train/extras/os_detect_darwin.rb +32 -0
  20. data/lib/train/extras/os_detect_linux.rb +148 -0
  21. data/lib/train/extras/os_detect_unix.rb +99 -0
  22. data/lib/train/extras/os_detect_windows.rb +57 -0
  23. data/lib/train/extras/stat.rb +133 -0
  24. data/lib/train/options.rb +80 -0
  25. data/lib/train/plugins.rb +40 -0
  26. data/lib/train/plugins/base_connection.rb +86 -0
  27. data/lib/train/plugins/transport.rb +49 -0
  28. data/lib/train/transports/docker.rb +103 -0
  29. data/lib/train/transports/local.rb +52 -0
  30. data/lib/train/transports/local_file.rb +90 -0
  31. data/lib/train/transports/local_os.rb +51 -0
  32. data/lib/train/transports/mock.rb +147 -0
  33. data/lib/train/transports/ssh.rb +163 -0
  34. data/lib/train/transports/ssh_connection.rb +225 -0
  35. data/lib/train/transports/winrm.rb +184 -0
  36. data/lib/train/transports/winrm_connection.rb +194 -0
  37. data/lib/train/version.rb +7 -0
  38. data/test/integration/.kitchen.yml +43 -0
  39. data/test/integration/Berksfile +3 -0
  40. data/test/integration/bootstrap.sh +17 -0
  41. data/test/integration/chefignore +1 -0
  42. data/test/integration/cookbooks/test/metadata.rb +1 -0
  43. data/test/integration/cookbooks/test/recipes/default.rb +100 -0
  44. data/test/integration/cookbooks/test/recipes/prep_files.rb +47 -0
  45. data/test/integration/docker_run.rb +153 -0
  46. data/test/integration/docker_test.rb +24 -0
  47. data/test/integration/docker_test_container.rb +24 -0
  48. data/test/integration/helper.rb +61 -0
  49. data/test/integration/sudo/customcommand.rb +15 -0
  50. data/test/integration/sudo/nopasswd.rb +16 -0
  51. data/test/integration/sudo/passwd.rb +21 -0
  52. data/test/integration/sudo/reqtty.rb +17 -0
  53. data/test/integration/sudo/run_as.rb +12 -0
  54. data/test/integration/test-travis-1.yaml +13 -0
  55. data/test/integration/test-travis-2.yaml +13 -0
  56. data/test/integration/test_local.rb +19 -0
  57. data/test/integration/test_ssh.rb +39 -0
  58. data/test/integration/tests/path_block_device_test.rb +74 -0
  59. data/test/integration/tests/path_character_device_test.rb +74 -0
  60. data/test/integration/tests/path_file_test.rb +79 -0
  61. data/test/integration/tests/path_folder_test.rb +90 -0
  62. data/test/integration/tests/path_missing_test.rb +77 -0
  63. data/test/integration/tests/path_pipe_test.rb +78 -0
  64. data/test/integration/tests/path_symlink_test.rb +95 -0
  65. data/test/integration/tests/run_command_test.rb +28 -0
  66. data/test/unit/extras/command_wrapper_test.rb +78 -0
  67. data/test/unit/extras/file_common_test.rb +180 -0
  68. data/test/unit/extras/linux_file_test.rb +167 -0
  69. data/test/unit/extras/os_common_test.rb +269 -0
  70. data/test/unit/extras/os_detect_linux_test.rb +189 -0
  71. data/test/unit/extras/os_detect_windows_test.rb +99 -0
  72. data/test/unit/extras/stat_test.rb +148 -0
  73. data/test/unit/extras/windows_file_test.rb +44 -0
  74. data/test/unit/helper.rb +7 -0
  75. data/test/unit/plugins/connection_test.rb +44 -0
  76. data/test/unit/plugins/transport_test.rb +111 -0
  77. data/test/unit/plugins_test.rb +22 -0
  78. data/test/unit/train_test.rb +156 -0
  79. data/test/unit/transports/local_file_test.rb +184 -0
  80. data/test/unit/transports/local_test.rb +87 -0
  81. data/test/unit/transports/mock_test.rb +87 -0
  82. data/test/unit/transports/ssh_test.rb +109 -0
  83. data/test/unit/version_test.rb +8 -0
  84. data/test/windows/local_test.rb +46 -0
  85. data/test/windows/winrm_test.rb +52 -0
  86. data/train.gemspec +38 -0
  87. metadata +295 -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,136 @@
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 = { # rubocop:disable Style/MutableConstant
38
+ 'redhat' => %w{
39
+ redhat oracle centos fedora amazon scientific xenserver wrlinux
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 omnios openindiana opensolaris nexentacore
52
+ },
53
+ 'windows' => %w{
54
+ windows
55
+ },
56
+ 'aix' => %w{
57
+ aix
58
+ },
59
+ 'hpux' => %w{
60
+ hpux
61
+ },
62
+ }
63
+
64
+ OS['linux'] = %w{linux alpine arch coreos exherbo gentoo slackware} + OS['redhat'] + OS['debian'] + OS['suse']
65
+
66
+ OS['unix'] = %w{unix aix hpux} + OS['linux'] + OS['solaris'] + OS['bsd']
67
+
68
+ # Helper methods to check the OS type
69
+ # Provides methods in the form of: linux?, unix?, solaris? ...
70
+ OS.keys.each do |os_family|
71
+ define_method((os_family + '?').to_sym) do
72
+ OS[os_family].include?(@platform[:family])
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def detect_family
79
+ # if some information is already defined, try to verify it
80
+ # with the remaining detection
81
+ unless @platform[:family].nil?
82
+ # return ok if the preconfigured family yielded a good result
83
+ return true if detect_family_type
84
+ # if not, reset the platform to presets and run the full detection
85
+ # TODO: print an error message in this case, as the instantiating
86
+ # backend is doing something wrong
87
+ @platform = {}
88
+ end
89
+
90
+ # TODO: extend base implementation for detecting the family type
91
+ # to Windows and others
92
+ case uname_s
93
+ when /linux/i
94
+ @platform[:family] = 'linux'
95
+ when /./
96
+ @platform[:family] = 'unix'
97
+ else
98
+ # Don't know what this is
99
+ @platform[:family] = nil
100
+ end
101
+
102
+ # try to detect the platform
103
+ return nil unless @platform[:family].nil?
104
+ detect_family_type
105
+ end
106
+
107
+ def detect_family_type
108
+ pf = @platform[:family]
109
+
110
+ return detect_windows if pf == 'windows'
111
+ return detect_darwin if pf == 'darwin'
112
+
113
+ if %w{freebsd netbsd openbsd aix solaris2 hpux}.include?(pf)
114
+ return detect_via_uname
115
+ end
116
+
117
+ # unix based systems combine the above
118
+ return true if pf == 'unix' and detect_darwin
119
+ return true if pf == 'unix' and detect_via_uname
120
+
121
+ # if we arrive here, we most likey have a regular linux
122
+ detect_linux
123
+ end
124
+
125
+ def get_config(path)
126
+ res = @backend.run_command("test -f #{path} && cat #{path}")
127
+ # ignore files that can't be read
128
+ return nil if res.exit_status != 0
129
+ res.stdout
130
+ end
131
+
132
+ def unix_file?(path)
133
+ @backend.run_command("test -f #{path}").exit_status == 0
134
+ end
135
+ end
136
+ 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,148 @@
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 # rubocop:disable Metrics/ModuleLength
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] = unix_file?('/usr/bin/raspi-config') ? 'raspbian' : 'debian'
34
+ @platform[:release] = raw.chomp
35
+ end
36
+ elsif !(raw = get_config('/etc/parallels-release')).nil?
37
+ @platform[:family] = redhatish_platform(raw)
38
+ @platform[:release] = raw[/(\d\.\d\.\d)/, 1]
39
+ elsif !(raw = get_config('/etc/redhat-release')).nil?
40
+ # TODO: Cisco
41
+ # TODO: fully investigate os-release and integrate it;
42
+ # here we just use it for centos
43
+ if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i
44
+ @platform[:family] = 'centos'
45
+ else
46
+ @platform[:family] = redhatish_platform(raw)
47
+ end
48
+ @platform[:release] = redhatish_version(raw)
49
+ elsif !(raw = get_config('/etc/system-release')).nil?
50
+ # Amazon Linux
51
+ @platform[:family] = redhatish_platform(raw)
52
+ @platform[:release] = redhatish_version(raw)
53
+ elsif !(suse = get_config('/etc/SuSE-release')).nil?
54
+ version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.')
55
+ version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == ''
56
+ @platform[:release] = version
57
+ @platform[:family] = 'suse'
58
+ @platform[:family] = 'opensuse' if suse =~ /^openSUSE/
59
+ elsif !(raw = get_config('/etc/arch-release')).nil?
60
+ @platform[:family] = 'arch'
61
+ # Because this is a rolling release distribution,
62
+ # use the kernel release, ex. 4.1.6-1-ARCH
63
+ @platform[:release] = uname_r
64
+ elsif !(raw = get_config('/etc/slackware-version')).nil?
65
+ @platform[:family] = 'slackware'
66
+ @platform[:release] = raw.scan(/(\d+|\.+)/).join
67
+ elsif !(raw = get_config('/etc/exherbo-release')).nil?
68
+ @platform[:family] = 'exherbo'
69
+ # Because this is a rolling release distribution,
70
+ # use the kernel release, ex. 4.1.6
71
+ @platform[:release] = uname_r
72
+ elsif !(raw = get_config('/etc/gentoo-release')).nil?
73
+ @platform[:family] = 'gentoo'
74
+ @platform[:release] = raw.scan(/(\d+|\.+)/).join
75
+ elsif !(raw = get_config('/etc/alpine-release')).nil?
76
+ @platform[:family] = 'alpine'
77
+ @platform[:release] = raw.strip
78
+ elsif !(raw = get_config('/etc/coreos/update.conf')).nil?
79
+ @platform[:family] = 'coreos'
80
+ meta = lsb_config(raw)
81
+ @platform[:release] = meta[:release]
82
+ elsif !(os_info = fetch_os_release).nil?
83
+ if os_info['ID_LIKE'] =~ /wrlinux/
84
+ @platform[:family] = 'wrlinux'
85
+ @platform[:release] = os_info['VERSION']
86
+ end
87
+ end
88
+
89
+ !@platform[:family].nil? && !@platform[:release].nil?
90
+ end
91
+
92
+ def uname_s
93
+ @uname_s ||= @backend.run_command('uname -s').stdout
94
+ end
95
+
96
+ def uname_r
97
+ @uname_r ||= (
98
+ res = @backend.run_command('uname -r').stdout
99
+ res.strip! unless res.nil?
100
+ res
101
+ )
102
+ end
103
+
104
+ def redhatish_platform(conf)
105
+ conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase
106
+ end
107
+
108
+ def redhatish_version(conf)
109
+ return conf[/((\d+) \(Rawhide\))/i, 1].downcase if conf[/rawhide/i]
110
+ return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i]
111
+ conf[/release ([\d\.]+)/, 1]
112
+ end
113
+
114
+ def detect_linux
115
+ # TODO: print an error in this step of the detection
116
+ return false if uname_s.nil? || uname_s.empty?
117
+ return false if uname_r.nil? || uname_r.empty?
118
+
119
+ return true if detect_linux_via_config
120
+ return true if detect_linux_via_lsb
121
+ # in all other cases we failed the detection
122
+ @platform[:family] = 'unknown'
123
+ end
124
+
125
+ def fetch_os_release
126
+ data = get_config('/etc/os-release')
127
+ return if data.nil?
128
+
129
+ os_info = parse_os_release_info(data)
130
+ cisco_info_file = os_info['CISCO_RELEASE_INFO']
131
+ if cisco_info_file
132
+ os_info.merge!(parse_os_release_info(get_config(cisco_info_file)))
133
+ end
134
+
135
+ os_info
136
+ end
137
+
138
+ def parse_os_release_info(raw)
139
+ return {} if raw.nil?
140
+
141
+ raw.lines.each_with_object({}) do |line, memo|
142
+ line.strip!
143
+ key, value = line.split('=', 2)
144
+ memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty?
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,99 @@
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
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
+ when /hp-ux/
24
+ @platform[:family] = 'hpux'
25
+ @platform[:name] = uname_s.lines[0].chomp
26
+ @platform[:release] = uname_r.lines[0].chomp
27
+
28
+ when /freebsd/
29
+ @platform[:family] = 'freebsd'
30
+ @platform[:name] = uname_s.lines[0].chomp
31
+ @platform[:release] = uname_r.lines[0].chomp
32
+
33
+ when /netbsd/
34
+ @platform[:family] = 'netbsd'
35
+ @platform[:name] = uname_s.lines[0].chomp
36
+ @platform[:release] = uname_r.lines[0].chomp
37
+
38
+ when /openbsd/
39
+ @platform[:family] = 'openbsd'
40
+ @platform[:name] = uname_s.lines[0].chomp
41
+ @platform[:release] = uname_r.lines[0].chomp
42
+
43
+ when /sunos/
44
+ detect_solaris
45
+ else
46
+ # in all other cases we didn't detect it
47
+ return false
48
+ end
49
+ # when we get here the detection returned a result
50
+ true
51
+ end
52
+
53
+ def detect_solaris # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
54
+ # read specific os name
55
+ # DEPRECATED: os[:family] is going to be deprecated, use os.solaris?
56
+ rel = get_config('/etc/release')
57
+ if /^.*(SmartOS).*$/ =~ rel
58
+ @platform[:name] = 'smartos'
59
+ @platform[:family] = 'smartos'
60
+ elsif !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil?
61
+ @platform[:name] = 'omnios'
62
+ @platform[:family] = 'omnios'
63
+ @platform[:release] = m[2]
64
+ elsif !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil?
65
+ @platform[:name] = 'openindiana'
66
+ @platform[:family] = 'openindiana'
67
+ @platform[:release] = m[2]
68
+ elsif /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel
69
+ @platform[:name] = 'opensolaris'
70
+ @platform[:family] = 'opensolaris'
71
+ @platform[:release] = m[2]
72
+ elsif !(m = /Oracle Solaris (\d+)/.match(rel)).nil?
73
+ # TODO: should be string!
74
+ @platform[:release] = m[1]
75
+ @platform[:name] = 'solaris'
76
+ @platform[:family] = 'solaris'
77
+ elsif /^\s*(Solaris)\s.*$/ =~ rel
78
+ @platform[:name] = 'solaris'
79
+ @platform[:family] = 'solaris'
80
+ elsif /^\s*(NexentaCore)\s.*$/ =~ rel
81
+ @platform[:name] = 'nexentacore'
82
+ @platform[:family] = 'nexentacore'
83
+ else
84
+ # unknown solaris
85
+ @platform[:name] = 'solaris_distro'
86
+ @platform[:family] = 'solaris'
87
+ end
88
+
89
+ # read release version
90
+ unless (version = /^5\.(?<release>\d+)$/.match(uname_r)).nil?
91
+ @platform[:release] = version['release']
92
+ end
93
+
94
+ # read architecture
95
+ arch = @backend.run_command('uname -p')
96
+ @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0
97
+ end
98
+ end
99
+ end