train 0.29.2 → 0.30.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -2
  3. data/lib/train.rb +1 -0
  4. data/lib/train/errors.rb +6 -0
  5. data/lib/train/extras.rb +0 -1
  6. data/lib/train/platforms.rb +82 -0
  7. data/lib/train/platforms/common.rb +34 -0
  8. data/lib/train/platforms/detect.rb +12 -0
  9. data/lib/train/platforms/detect/helpers/os_common.rb +56 -0
  10. data/lib/train/platforms/detect/helpers/os_linux.rb +75 -0
  11. data/lib/train/{extras/os_detect_windows.rb → platforms/detect/helpers/os_windows.rb} +3 -10
  12. data/lib/train/platforms/detect/scanner.rb +84 -0
  13. data/lib/train/platforms/detect/specifications/os.rb +480 -0
  14. data/lib/train/platforms/family.rb +26 -0
  15. data/lib/train/platforms/platform.rb +80 -0
  16. data/lib/train/plugins/base_connection.rb +75 -27
  17. data/lib/train/transports/docker.rb +17 -28
  18. data/lib/train/transports/local.rb +21 -22
  19. data/lib/train/transports/mock.rb +44 -30
  20. data/lib/train/transports/ssh_connection.rb +55 -67
  21. data/lib/train/transports/winrm_connection.rb +16 -26
  22. data/lib/train/version.rb +1 -1
  23. data/test/unit/file/remote/linux_test.rb +2 -2
  24. data/test/unit/platforms/detect/os_common_test.rb +85 -0
  25. data/test/unit/platforms/detect/os_linux_test.rb +124 -0
  26. data/test/unit/{extras/os_detect_windows_test.rb → platforms/detect/os_windows_test.rb} +5 -2
  27. data/test/unit/platforms/detect/scanner_test.rb +61 -0
  28. data/test/unit/platforms/family_test.rb +32 -0
  29. data/test/unit/platforms/os_detect_test.rb +175 -0
  30. data/test/unit/{extras/os_common_test.rb → platforms/platform_test.rb} +103 -18
  31. data/test/unit/platforms/platforms_test.rb +42 -0
  32. data/test/unit/plugins/connection_test.rb +106 -8
  33. data/test/unit/transports/local_test.rb +20 -15
  34. data/test/unit/transports/mock_test.rb +16 -6
  35. data/test/unit/transports/ssh_test.rb +17 -15
  36. metadata +28 -19
  37. data/lib/train/extras/linux_lsb.rb +0 -60
  38. data/lib/train/extras/os_common.rb +0 -151
  39. data/lib/train/extras/os_detect_arista_eos.rb +0 -34
  40. data/lib/train/extras/os_detect_darwin.rb +0 -40
  41. data/lib/train/extras/os_detect_esx.rb +0 -22
  42. data/lib/train/extras/os_detect_linux.rb +0 -164
  43. data/lib/train/extras/os_detect_openvms.rb +0 -29
  44. data/lib/train/extras/os_detect_unix.rb +0 -106
  45. data/lib/train/extras/uname.rb +0 -28
  46. data/lib/train/transports/local_os.rb +0 -51
  47. data/test/unit/extras/os_detect_linux_test.rb +0 -230
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 91f83eccb563ed719ca19fd8be06d9616afc4eae
4
- data.tar.gz: 740ba4a80a9b4690b36dec69dd2e752659032033
3
+ metadata.gz: 0e4971c8f699dbb772e9eb5532fa2de1771de4a5
4
+ data.tar.gz: 40bf72dd2c53b33379c256b4a4f4acc42dc46efd
5
5
  SHA512:
6
- metadata.gz: 96eda4528ad55102f10ff705b13a639dec7405f693f67f72d6d8d1b11c35439210ca92ee172d3f18ae564097271de3aa88671f2c8528a8a5b84604dd1ec0344d
7
- data.tar.gz: b4260ea407f3cf12614d222d5304a85091830972aade03e10491f1b897f5e01c99e3dc7ff45a7314cdd30e7224d46cd0b460538d34a0454f0601136ff8343db8
6
+ metadata.gz: 3c5fdd0a5d53e68d3eb928d5e9ddcbcaea5a8c3fa39b57d7aa3a20f7135f4436d78e334e4d93b3ce1e8f2fd2a3310462d10ae4099899372bd4f4fbd6309af4e4
7
+ data.tar.gz: db6f8f5c82f459a9f78c60cf758aaad8c4877e5adee714add3e060e5328c85a4275ef3502aae0525d80a49b0100b92fea2345f5f493fddf2ecc766954dae9c44
@@ -1,10 +1,34 @@
1
1
  # Change Log
2
2
 
3
- ## [0.29.1](https://github.com/chef/train/tree/0.29.1) (2017-11-13)
4
- [Full Changelog](https://github.com/chef/train/compare/v0.29.0...0.29.1)
3
+ ## [0.30.0](https://github.com/chef/train/tree/0.30.0) (2017-12-04)
4
+ [Full Changelog](https://github.com/chef/train/compare/v0.29.2...0.30.0)
5
5
 
6
6
  **Merged pull requests:**
7
7
 
8
+ - Change the mock transport name to be 'mock' [\#221](https://github.com/chef/train/pull/221) ([jquick](https://github.com/jquick))
9
+ - Enable caching on connections [\#214](https://github.com/chef/train/pull/214) ([jquick](https://github.com/jquick))
10
+
11
+ ## [v0.29.2](https://github.com/chef/train/tree/v0.29.2) (2017-11-21)
12
+ [Full Changelog](https://github.com/chef/train/compare/v0.29.1...v0.29.2)
13
+
14
+ **Fixed bugs:**
15
+
16
+ - Add unix\_mode\_mask method to Train::File::Local::Unix [\#215](https://github.com/chef/train/pull/215) ([adamleff](https://github.com/adamleff))
17
+
18
+ **Merged pull requests:**
19
+
20
+ - Fix regressions in 0.29.1 [\#219](https://github.com/chef/train/pull/219) ([adamleff](https://github.com/adamleff))
21
+ - Use the sanitized file path for remote linux files [\#218](https://github.com/chef/train/pull/218) ([RoboticCheese](https://github.com/RoboticCheese))
22
+ - Remove bundler install during Appveyor tests [\#217](https://github.com/chef/train/pull/217) ([adamleff](https://github.com/adamleff))
23
+ - Fix inspec mock tests [\#216](https://github.com/chef/train/pull/216) ([jquick](https://github.com/jquick))
24
+ - Platform framework and detect DSL [\#209](https://github.com/chef/train/pull/209) ([jquick](https://github.com/jquick))
25
+
26
+ ## [v0.29.1](https://github.com/chef/train/tree/v0.29.1) (2017-11-13)
27
+ [Full Changelog](https://github.com/chef/train/compare/v0.29.0...v0.29.1)
28
+
29
+ **Merged pull requests:**
30
+
31
+ - Release 0.29.1 [\#213](https://github.com/chef/train/pull/213) ([adamleff](https://github.com/adamleff))
8
32
  - Allow for a nil value when mocking OS [\#212](https://github.com/chef/train/pull/212) ([adamleff](https://github.com/adamleff))
9
33
  - Ensure a `mounted?` method exists for all File classes, including Mock [\#211](https://github.com/chef/train/pull/211) ([adamleff](https://github.com/adamleff))
10
34
 
@@ -6,6 +6,7 @@ require 'train/version'
6
6
  require 'train/options'
7
7
  require 'train/plugins'
8
8
  require 'train/errors'
9
+ require 'train/platforms'
9
10
  require 'uri'
10
11
 
11
12
  module Train
@@ -20,4 +20,10 @@ module Train
20
20
  # Base exception class for all exceptions that are caused by other failures
21
21
  # in the transport layer.
22
22
  class TransportError < ::StandardError; end
23
+
24
+ # Exception for when no platform can be detected.
25
+ class PlatformDetectionFailed < ::StandardError; end
26
+
27
+ # Exception for when a invalid cache type is passed.
28
+ class UnknownCacheType < ::StandardError; end
23
29
  end
@@ -4,7 +4,6 @@
4
4
 
5
5
  module Train::Extras
6
6
  require 'train/extras/command_wrapper'
7
- require 'train/extras/os_common'
8
7
  require 'train/extras/stat'
9
8
 
10
9
  CommandResult = Struct.new(:stdout, :stderr, :exit_status)
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+
3
+ require 'train/platforms/common'
4
+ require 'train/platforms/family'
5
+ require 'train/platforms/platform'
6
+ require 'train/platforms/detect'
7
+ require 'train/platforms/detect/scanner'
8
+ require 'train/platforms/detect/specifications/os'
9
+
10
+ module Train::Platforms
11
+ class << self
12
+ # Retrieve the current platform list
13
+ #
14
+ # @return [Hash] map with platform names and their objects
15
+ def list
16
+ @list ||= {}
17
+ end
18
+
19
+ # Retrieve the current family list
20
+ #
21
+ # @return [Hash] map with family names and their objects
22
+ def families
23
+ @families ||= {}
24
+ end
25
+ end
26
+
27
+ # Create or update a platform
28
+ #
29
+ # @return Train::Platform
30
+ def self.name(name, condition = {})
31
+ # Check the list to see if one is already created
32
+ plat = list[name]
33
+ unless plat.nil?
34
+ # Pass the condition incase we are adding a family relationship
35
+ plat.condition = condition unless condition.nil?
36
+ return plat
37
+ end
38
+
39
+ Train::Platforms::Platform.new(name, condition)
40
+ end
41
+
42
+ # Create or update a family
43
+ #
44
+ # @return Train::Platforms::Family
45
+ def self.family(name, condition = {})
46
+ # Check the families to see if one is already created
47
+ family = families[name]
48
+ unless family.nil?
49
+ # Pass the condition incase we are adding a family relationship
50
+ family.condition = condition unless condition.nil?
51
+ return family
52
+ end
53
+
54
+ Train::Platforms::Family.new(name, condition)
55
+ end
56
+
57
+ # Find the families or top level platforms
58
+ #
59
+ # @return [Hash] with top level family and platforms
60
+ def self.top_platforms
61
+ top_platforms = list.select { |_key, value| value.families.empty? }
62
+ top_platforms.merge!(families.select { |_key, value| value.families.empty? })
63
+ top_platforms
64
+ end
65
+
66
+ # List all platforms and families in a readable output
67
+ def self.list_all
68
+ top_platforms = self.top_platforms
69
+ top_platforms.each_value do |platform|
70
+ puts platform.title
71
+ print_children(platform) if defined?(platform.children)
72
+ end
73
+ end
74
+
75
+ def self.print_children(parent, pad = 2)
76
+ parent.children.each do |key, value|
77
+ obj = key
78
+ puts "#{' ' * pad}-> #{obj.title}#{value unless value.empty?}"
79
+ print_children(obj, pad + 2) if defined?(obj.children) && !obj.children.nil?
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ module Train::Platforms
4
+ module Common
5
+ # Add a family connection. This will create a family
6
+ # if it does not exist and add a child relationship.
7
+ def in_family(family)
8
+ if self.class == Train::Platforms::Family && @name == family
9
+ fail "Unable to add family #{@name} to itself: '#{@name}.in_family(#{family})'"
10
+ end
11
+
12
+ # add family to the family list
13
+ family = Train::Platforms.family(family)
14
+ family.children[self] = @condition
15
+
16
+ @families[family] = @condition
17
+ @condition = nil
18
+ self
19
+ end
20
+
21
+ def detect(&block)
22
+ if block_given?
23
+ @detect = block
24
+ self
25
+ elsif @detect.nil?
26
+ # we are returning a block that just returns false here
27
+ # to skip the family/platform evaluation if detect is not set
28
+ ->(_) { false }
29
+ else
30
+ @detect
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module Train::Platforms
4
+ module Detect
5
+ # Main detect method to scan all platforms for a match
6
+ #
7
+ # @return Train::Platform instance or error if none found
8
+ def self.scan(backend)
9
+ Scanner.new(backend).scan
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require 'train/platforms/detect/helpers/os_linux'
4
+ require 'train/platforms/detect/helpers/os_windows'
5
+ require 'rbconfig'
6
+
7
+ module Train::Platforms::Detect::Helpers
8
+ module OSCommon
9
+ include Train::Platforms::Detect::Helpers::Linux
10
+ include Train::Platforms::Detect::Helpers::Windows
11
+
12
+ def ruby_host_os(regex)
13
+ ::RbConfig::CONFIG['host_os'] =~ regex
14
+ end
15
+
16
+ def winrm?
17
+ Object.const_defined?('Train::Transports::WinRM::Connection') &&
18
+ @backend.class == Train::Transports::WinRM::Connection
19
+ end
20
+
21
+ def unix_file_contents(path)
22
+ # keep a log of files incase multiple checks call the same one
23
+ return @files[path] if @files.key?(path)
24
+
25
+ res = @backend.run_command("test -f #{path} && cat #{path}")
26
+ # ignore files that can't be read
27
+ @files[path] = res.exit_status.zero? ? res.stdout : nil
28
+ @files[path]
29
+ end
30
+
31
+ def unix_file_exist?(path)
32
+ @backend.run_command("test -f #{path}").exit_status.zero?
33
+ end
34
+
35
+ def command_output(cmd)
36
+ res = @backend.run_command(cmd).stdout
37
+ res.strip! unless res.nil?
38
+ res
39
+ end
40
+
41
+ def unix_uname_s
42
+ return @uname[:s] if @uname.key?(:s)
43
+ @uname[:s] = command_output('uname -s')
44
+ end
45
+
46
+ def unix_uname_r
47
+ return @uname[:r] if @uname.key?(:r)
48
+ @uname[:r] = command_output('uname -r')
49
+ end
50
+
51
+ def unix_uname_m
52
+ return @uname[:m] if @uname.key?(:m)
53
+ @uname[:m] = command_output('uname -m')
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ module Train::Platforms::Detect::Helpers
4
+ module Linux
5
+ def redhatish_platform(conf)
6
+ conf =~ /^red hat/i ? 'redhat' : /(\w+)/i.match(conf)[1].downcase
7
+ end
8
+
9
+ def redhatish_version(conf)
10
+ case conf
11
+ when /rawhide/i
12
+ /((\d+) \(Rawhide\))/i.match(conf)[1].downcase
13
+ when /derived from .*linux/i
14
+ /Linux ((\d+|\.)+)/i.match(conf)[1]
15
+ else
16
+ /release ([\d\.]+)/.match(conf)[1]
17
+ end
18
+ end
19
+
20
+ def linux_os_release
21
+ data = unix_file_contents('/etc/os-release')
22
+ return if data.nil?
23
+
24
+ os_info = parse_os_release_info(data)
25
+ cisco_info_file = os_info['CISCO_RELEASE_INFO']
26
+ if cisco_info_file
27
+ os_info.merge!(parse_os_release_info(unix_file_contents(cisco_info_file)))
28
+ end
29
+
30
+ os_info
31
+ end
32
+
33
+ def parse_os_release_info(raw)
34
+ return {} if raw.nil?
35
+
36
+ raw.lines.each_with_object({}) do |line, memo|
37
+ line.strip!
38
+ next if line.empty?
39
+ key, value = line.split('=', 2)
40
+ memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty?
41
+ end
42
+ end
43
+
44
+ def lsb_config(content)
45
+ id = /^DISTRIB_ID=["']?(.+?)["']?$/.match(content)
46
+ release = /^DISTRIB_RELEASE=["']?(.+?)["']?$/.match(content)
47
+ codename = /^DISTRIB_CODENAME=["']?(.+?)["']?$/.match(content)
48
+ {
49
+ id: id.nil? ? nil : id[1],
50
+ release: release.nil? ? nil : release[1],
51
+ codename: codename.nil? ? nil : codename[1],
52
+ }
53
+ end
54
+
55
+ def lsb_release(content)
56
+ id = /^Distributor ID:\s+(.+)$/.match(content)
57
+ release = /^Release:\s+(.+)$/.match(content)
58
+ codename = /^Codename:\s+(.+)$/.match(content)
59
+ {
60
+ id: id.nil? ? nil : id[1],
61
+ release: release.nil? ? nil : release[1],
62
+ codename: codename.nil? ? nil : codename[1],
63
+ }
64
+ end
65
+
66
+ def read_linux_lsb
67
+ return @lsb unless @lsb.empty?
68
+ if !(raw = unix_file_contents('/etc/lsb-release')).nil?
69
+ @lsb = lsb_config(raw)
70
+ elsif !(raw = unix_file_contents('/usr/bin/lsb-release')).nil?
71
+ @lsb = lsb_release(raw)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,14 +1,7 @@
1
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
- module Train::Extras
11
- module DetectWindows
2
+
3
+ module Train::Platforms::Detect::Helpers
4
+ module Windows
12
5
  def detect_windows
13
6
  res = @backend.run_command('cmd /c ver')
14
7
  return false if res.exit_status != 0 or res.stdout.empty?
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ require 'train/platforms/detect/helpers/os_common'
4
+
5
+ module Train::Platforms::Detect
6
+ class Scanner
7
+ include Train::Platforms::Detect::Helpers::OSCommon
8
+
9
+ def initialize(backend)
10
+ @backend = backend
11
+ @platform = {}
12
+ @family_hierarchy = []
13
+
14
+ # detect cache variables
15
+ @files = {}
16
+ @uname = {}
17
+ @lsb = {}
18
+ end
19
+
20
+ # Main detect method to scan all platforms for a match
21
+ #
22
+ # @return Train::Platform instance or error if none found
23
+ def scan
24
+ # start with the platform/families who have no families (the top levels)
25
+ top = Train::Platforms.top_platforms
26
+ top.each do |_name, plat|
27
+ # we are doing a instance_eval here to make sure we have the proper
28
+ # context with all the detect helper methods
29
+ next unless instance_eval(&plat.detect) == true
30
+
31
+ # if we have a match start looking at the children
32
+ plat_result = scan_children(plat)
33
+ next if plat_result.nil?
34
+
35
+ # return platform to backend
36
+ @family_hierarchy << plat.name
37
+ return get_platform(plat_result)
38
+ end
39
+
40
+ fail Train::PlatformDetectionFailed, 'Sorry, we are unable to detect your platform'
41
+ end
42
+
43
+ def scan_children(parent)
44
+ parent.children.each do |plat, condition|
45
+ next unless instance_eval(&plat.detect) == true
46
+
47
+ if plat.class == Train::Platforms::Platform
48
+ @platform[:family] = parent.name
49
+ return plat if condition.empty? || check_condition(condition)
50
+ elsif plat.class == Train::Platforms::Family
51
+ plat = scan_family_children(plat)
52
+ return plat unless plat.nil?
53
+ end
54
+ end
55
+
56
+ nil
57
+ end
58
+
59
+ def scan_family_children(plat)
60
+ child_result = scan_children(plat) unless plat.children.nil?
61
+ return if child_result.nil?
62
+ @family_hierarchy << plat.name
63
+ child_result
64
+ end
65
+
66
+ def check_condition(condition)
67
+ condition.each do |k, v|
68
+ op, expected = v.strip.split(' ')
69
+ op = '==' if op == '='
70
+ return false if @platform[k].nil? || !instance_eval("'#{@platform[k]}' #{op} '#{expected}'")
71
+ end
72
+
73
+ true
74
+ end
75
+
76
+ def get_platform(plat)
77
+ plat.backend = @backend
78
+ plat.platform = @platform
79
+ plat.add_platform_methods
80
+ plat.family_hierarchy = @family_hierarchy
81
+ plat
82
+ end
83
+ end
84
+ end