train 0.29.2 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
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