train 0.12.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 (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