From dd0b84812fd577ffcaaf800ed46f368f2bf97700 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Fri, 13 Oct 2017 09:02:46 -0400 Subject: [PATCH 01/22] Initial building block for platforms --- Gemfile | 1 + lib/train.rb | 1 + lib/train/extras/command_wrapper.rb | 4 +- lib/train/platform.rb | 80 ++++++++++++++++++++++++++ lib/train/plugins/base_connection.rb | 4 +- lib/train/transports/ssh_connection.rb | 4 +- 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 lib/train/platform.rb diff --git a/Gemfile b/Gemfile index ed43bede..a1123d67 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ group :test do gem 'rubocop', '~> 0.36.0' gem 'simplecov', '~> 0.10' gem 'concurrent-ruby', '~> 0.9' + gem 'pry-byebug' end group :integration do diff --git a/lib/train.rb b/lib/train.rb index 6a85b7bb..1de1f558 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -6,6 +6,7 @@ require 'train/options' require 'train/plugins' require 'train/errors' +require 'train/platform' require 'uri' module Train diff --git a/lib/train/extras/command_wrapper.rb b/lib/train/extras/command_wrapper.rb index 716d1796..fb2562ed 100644 --- a/lib/train/extras/command_wrapper.rb +++ b/lib/train/extras/command_wrapper.rb @@ -162,7 +162,7 @@ class CommandWrapper include_options LinuxCommand def self.load(transport, options) - if transport.os.unix? + if transport.platform.arch == 'unix' return nil unless LinuxCommand.active?(options) res = LinuxCommand.new(transport, options) msg = res.verify @@ -170,7 +170,7 @@ def self.load(transport, options) res # only use powershell command for local transport. winrm transport # uses powershell as default - elsif transport.os.windows? && transport.class == Train::Transports::Local::Connection + elsif transport.platform.windows? && transport.class == Train::Transports::Local::Connection PowerShellCommand.new(transport, options) end end diff --git a/lib/train/platform.rb b/lib/train/platform.rb new file mode 100644 index 00000000..037399b1 --- /dev/null +++ b/lib/train/platform.rb @@ -0,0 +1,80 @@ +# encoding: utf-8 +# +# Author:: Jared Quick + +require 'train/platform/common' +require 'train/platform/info' +require 'train/platform/family' + +module Train::Platform + class << self + # Retrieve the current family list, containing all family names + # and their platform info objects. + # + # @return [Hash] map with family names and their children + def list + @list ||= {} + end + + # This is a holding zone for platforms until they have a + # proper family association + def families + @families ||= {} + end + end + + # Create or update a platform + def self.name(name, condition = {}) + # Check the list to see if we already have one + plat = Train::Platform.list[name] + unless plat.nil? + plat.condition = condition unless condition.nil? + return plat + end + + Train::Platform::Info.new(name, condition) + end + + # Create or update a family + def self.family(name, condition = {}) + # Check the families to see if we already have one + family = Train::Platform.families[name] + unless family.nil? + family.condition = condition unless condition.nil? + return family + end + + Train::Platform::Family.new(name, condition) + end + + def self.top_platforms + top_platforms = families.select { |_key, value| value.families.empty? } + top_platforms.merge!(list.select { |_key, value| value.families.empty? }) + top_platforms + end + + def self.list_all + top_platforms = self.top_platforms + top_platforms.each_value do |platform| + puts "#{platform.title} (#{platform.class})" + print_children(platform) if defined?(platform.children) + end + end + + def self.print_children(parent, pad = 2) + parent.children.each do |key, value| + obj = key + puts "#{' ' * pad}-> #{obj.title}#{value unless value.empty?}" + print_children(obj, pad + 2) unless !defined?(obj.children) || obj.children.nil? + end + end + + require 'train/platform/specs/linux' + def self.detect(backend) + @backend = backend + instance_eval(&Train::Platform.family('linux').detect) + Train::Platform.name('ubuntu').backend = @backend + Train::Platform.name('ubuntu').arch = 'unix' + Train::Platform.name('ubuntu') + end +end diff --git a/lib/train/plugins/base_connection.rb b/lib/train/plugins/base_connection.rb index e621fcf3..92af6419 100644 --- a/lib/train/plugins/base_connection.rb +++ b/lib/train/plugins/base_connection.rb @@ -60,8 +60,8 @@ def run_command(_command) # Get information on the operating system which this transport connects to. # # @return [OSCommon] operating system information - def os - fail Train::ClientError, "#{self.class} does not implement #os()" + def platform + fail Train::ClientError, "#{self.class} does not implement #platform()" end # Interact with files on the target. Read, write, and get metadata diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index b6f97df9..8f694da3 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -55,8 +55,8 @@ def close @session = nil end - def os - @os ||= OS.new(self) + def platform + @platform ||= Train::Platform.detect(self) end def file(path) From b4aac004741553e60c29a93ee3d5d030c79a2395 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Fri, 13 Oct 2017 09:04:21 -0400 Subject: [PATCH 02/22] Refactor platform classes and deprecate os Signed-off-by: Jared Quick --- lib/train.rb | 2 +- lib/train/extras/command_wrapper.rb | 4 +- lib/train/platform.rb | 92 ++++++-------------------- lib/train/platforms.rb | 90 +++++++++++++++++++++++++ lib/train/platforms/common.rb | 40 +++++++++++ lib/train/platforms/detect.rb | 10 +++ lib/train/platforms/family.rb | 31 +++++++++ lib/train/platforms/specs/os.rb | 22 ++++++ lib/train/plugins/base_connection.rb | 7 ++ lib/train/transports/ssh_connection.rb | 14 ++-- 10 files changed, 231 insertions(+), 81 deletions(-) create mode 100644 lib/train/platforms.rb create mode 100644 lib/train/platforms/common.rb create mode 100644 lib/train/platforms/detect.rb create mode 100644 lib/train/platforms/family.rb create mode 100644 lib/train/platforms/specs/os.rb diff --git a/lib/train.rb b/lib/train.rb index 1de1f558..c85e90cf 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -6,7 +6,7 @@ require 'train/options' require 'train/plugins' require 'train/errors' -require 'train/platform' +require 'train/platforms' require 'uri' module Train diff --git a/lib/train/extras/command_wrapper.rb b/lib/train/extras/command_wrapper.rb index fb2562ed..ac49e966 100644 --- a/lib/train/extras/command_wrapper.rb +++ b/lib/train/extras/command_wrapper.rb @@ -162,7 +162,7 @@ class CommandWrapper include_options LinuxCommand def self.load(transport, options) - if transport.platform.arch == 'unix' + if transport.os.arch == 'unix' return nil unless LinuxCommand.active?(options) res = LinuxCommand.new(transport, options) msg = res.verify @@ -170,7 +170,7 @@ def self.load(transport, options) res # only use powershell command for local transport. winrm transport # uses powershell as default - elsif transport.platform.windows? && transport.class == Train::Transports::Local::Connection + elsif transport.os.windows? && transport.class == Train::Transports::Local::Connection PowerShellCommand.new(transport, options) end end diff --git a/lib/train/platform.rb b/lib/train/platform.rb index 037399b1..0abfd811 100644 --- a/lib/train/platform.rb +++ b/lib/train/platform.rb @@ -2,79 +2,29 @@ # # Author:: Jared Quick -require 'train/platform/common' -require 'train/platform/info' -require 'train/platform/family' - -module Train::Platform - class << self - # Retrieve the current family list, containing all family names - # and their platform info objects. - # - # @return [Hash] map with family names and their children - def list - @list ||= {} - end - - # This is a holding zone for platforms until they have a - # proper family association - def families - @families ||= {} - end - end - - # Create or update a platform - def self.name(name, condition = {}) - # Check the list to see if we already have one - plat = Train::Platform.list[name] - unless plat.nil? - plat.condition = condition unless condition.nil? - return plat +module Train + class Platform + include Train::Platforms::Common + attr_accessor :name, :condition, :families + + def initialize(name, condition = {}) + @condition = condition + @name = name + @families = {} + + # add itself to the platform list + Train::Platforms.list[name] = self end - Train::Platform::Info.new(name, condition) - end - - # Create or update a family - def self.family(name, condition = {}) - # Check the families to see if we already have one - family = Train::Platform.families[name] - unless family.nil? - family.condition = condition unless condition.nil? - return family + def title(title = nil) + if @title.nil? && title.nil? + name.capitalize + elsif title.nil? + @title + else + @title = title + self + end end - - Train::Platform::Family.new(name, condition) - end - - def self.top_platforms - top_platforms = families.select { |_key, value| value.families.empty? } - top_platforms.merge!(list.select { |_key, value| value.families.empty? }) - top_platforms - end - - def self.list_all - top_platforms = self.top_platforms - top_platforms.each_value do |platform| - puts "#{platform.title} (#{platform.class})" - print_children(platform) if defined?(platform.children) - end - end - - def self.print_children(parent, pad = 2) - parent.children.each do |key, value| - obj = key - puts "#{' ' * pad}-> #{obj.title}#{value unless value.empty?}" - print_children(obj, pad + 2) unless !defined?(obj.children) || obj.children.nil? - end - end - - require 'train/platform/specs/linux' - def self.detect(backend) - @backend = backend - instance_eval(&Train::Platform.family('linux').detect) - Train::Platform.name('ubuntu').backend = @backend - Train::Platform.name('ubuntu').arch = 'unix' - Train::Platform.name('ubuntu') end end diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb new file mode 100644 index 00000000..56954e5a --- /dev/null +++ b/lib/train/platforms.rb @@ -0,0 +1,90 @@ +# encoding: utf-8 +# +# Author:: Jared Quick + +require 'train/platforms/common' +require 'train/platforms/family' +require 'train/platform' + +module Train::Platforms + class << self + # Retrieve the current platform list + # + # @return [Hash] map with platform names and their objects + def list + @list ||= {} + end + + # Retrieve the current family list + # + # @return [Hash] map with family names and their objects + def families + @families ||= {} + end + end + + # Create or update a platform + # + # @return Train::Platform + def self.name(name, condition = {}) + # Check the list to see if one is already created + plat = list[name] + unless plat.nil? + # Pass the condition incase we are adding a family relationship + plat.condition = condition unless condition.nil? + return plat + end + + Train::Platform.new(name, condition) + end + + # Create or update a family + # + # @return Train::Platforms::Family + def self.family(name, condition = {}) + # Check the families to see if one is already created + family = families[name] + unless family.nil? + # Pass the condition incase we are adding a family relationship + family.condition = condition unless condition.nil? + return family + end + + Train::Platforms::Family.new(name, condition) + end + + # Find the families or top level platforms + # + # @return [Hash] with top level family and platforms + def self.top_platforms + top_platforms = families.select { |_key, value| value.families.empty? } + top_platforms.merge!(list.select { |_key, value| value.families.empty? }) + top_platforms + end + + # List all platforms and families in a readable output + def self.list_all + top_platforms = self.top_platforms + top_platforms.each_value do |platform| + puts "#{platform.title} (#{platform.class})" + print_children(platform) if defined?(platform.children) + end + end + + def self.print_children(parent, pad = 2) + parent.children.each do |key, value| + obj = key + puts "#{' ' * pad}-> #{obj.title}#{value unless value.empty?}" + print_children(obj, pad + 2) unless !defined?(obj.children) || obj.children.nil? + end + end + + require 'train/platforms/specs/os' + def self.detect(backend) + @backend = backend + instance_eval(&Train::Platforms.family('linux').detect) + Train::Platforms.name('ubuntu').backend = @backend + Train::Platforms.name('ubuntu').arch = 'unix' + Train::Platforms.name('ubuntu') + end +end diff --git a/lib/train/platforms/common.rb b/lib/train/platforms/common.rb new file mode 100644 index 00000000..778c359c --- /dev/null +++ b/lib/train/platforms/common.rb @@ -0,0 +1,40 @@ +# encoding: utf-8 +# +# Author:: Jared Quick + +module Train::Platforms + module Common + + # Add a family connection. This will create a family + # if it does not exist and add a child relationship. + def is_a(family) + if self.class == Train::Platforms::Family && @name == family + raise "Sorry you can not add a family inside itself '#{@name}.is_a(#{family})'" + end + + # add family to the family list + family = Train::Platforms.family(family) + family.children[self] = @condition + + @families[family] = @condition + @condition = nil + self + end + + def detect(&block) + return @detect if block.nil? + @detect = block + self + end + + def method_missing(m, *args, &block) + unless args.empty? + args = args.first if args.size == 1 + instance_variable_set("@#{m.to_s.chomp('=')}", args) + self + else + instance_variable_get("@#{m}") + end + end + end +end diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb new file mode 100644 index 00000000..066977e1 --- /dev/null +++ b/lib/train/platforms/detect.rb @@ -0,0 +1,10 @@ +# encoding: utf-8 +# +# Author:: Jared Quick + +require 'train/platform/specs/linux' + +module Train::Detect + + +end diff --git a/lib/train/platforms/family.rb b/lib/train/platforms/family.rb new file mode 100644 index 00000000..f6dc5d7a --- /dev/null +++ b/lib/train/platforms/family.rb @@ -0,0 +1,31 @@ +# encoding: utf-8 +# +# Author:: Jared Quick + +module Train::Platforms + class Family + include Train::Platforms::Common + attr_accessor :name, :condition, :families, :children + + def initialize(name, condition) + @name = name + @condition = {} + @families = {} + @children = {} + + # add itself to the families list + Train::Platforms.families[@name.to_s] = self + end + + def title(title = nil) + if @title.nil? && title.nil? + "#{name.capitalize} Family" + elsif title.nil? + @title + else + @title = title + self + end + end + end +end diff --git a/lib/train/platforms/specs/os.rb b/lib/train/platforms/specs/os.rb new file mode 100644 index 00000000..973dd66b --- /dev/null +++ b/lib/train/platforms/specs/os.rb @@ -0,0 +1,22 @@ +platform = Train::Platforms + +# linux +platform.family('linux') + .is_a('unix') + .detect { + puts @backend.run_command('uname -s').stdout + } + +# debian +platform.family('debian').is_a('linux') +platform.name('debian').title('Debian Linux').is_a('debian') +platform.name('ubuntu').is_a('debian').title('Ubuntu Linux') +platform.name('linuxmint').title('LinuxMint').is_a('debian').release('7.0') +platform.name('backtrack', release: '>= 4').is_a('debian') + +# redhat +platform.family('redhat').is_a('linux') +platform.name('centos').title('Centos Linux').is_a('redhat') +platform.name('rhel').title('Red Hat Enterprise Linux').is_a('redhat') +platform.name('oracle').title('Oracle Linux').is_a('redhat') +platform.name('awz').title('Amazon Linux').is_a('redhat') diff --git a/lib/train/plugins/base_connection.rb b/lib/train/plugins/base_connection.rb index 92af6419..fe246d49 100644 --- a/lib/train/plugins/base_connection.rb +++ b/lib/train/plugins/base_connection.rb @@ -60,6 +60,13 @@ def run_command(_command) # Get information on the operating system which this transport connects to. # # @return [OSCommon] operating system information + def os + fail Train::ClientError, "#{self.class} does not implement #os()" + end + + # Get information on the operating system which this transport connects to. + # + # @return [Platform] system information def platform fail Train::ClientError, "#{self.class} does not implement #platform()" end diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index 8f694da3..6b4edfe1 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -55,8 +55,14 @@ def close @session = nil end + def os + warn '[DEPRECATION] `os` is being deprecated. ' \ + 'Please use `platform` instead.' + platform + end + def platform - @platform ||= Train::Platform.detect(self) + @platform ||= Train::Platforms.detect(self) end def file(path) @@ -245,11 +251,5 @@ def to_s options_to_print[:password] = '' if options_to_print.key?(:password) "#{@username}@#{@hostname}<#{options_to_print.inspect}>" end - - class OS < OSCommon - def initialize(backend) - super(backend) - end - end end end From 62b9d11dad25237eae3e9d69463e97b576a96e2f Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Fri, 13 Oct 2017 16:41:23 -0400 Subject: [PATCH 03/22] Add basic detect logic Signed-off-by: Jared Quick --- lib/train.rb | 1 + lib/train/extras/command_wrapper.rb | 4 +- lib/train/platforms.rb | 9 -- lib/train/platforms/detect.rb | 54 +++++++- lib/train/platforms/detect/linux_lsb.rb | 53 ++++++++ lib/train/platforms/detect/os_common.rb | 76 +++++++++++ lib/train/platforms/detect/os_linux.rb | 164 ++++++++++++++++++++++++ lib/train/platforms/detect/uname.rb | 21 +++ lib/train/platforms/specs/os.rb | 134 ++++++++++++++++--- lib/train/transports/ssh_connection.rb | 2 +- 10 files changed, 486 insertions(+), 32 deletions(-) create mode 100644 lib/train/platforms/detect/linux_lsb.rb create mode 100644 lib/train/platforms/detect/os_common.rb create mode 100644 lib/train/platforms/detect/os_linux.rb create mode 100644 lib/train/platforms/detect/uname.rb diff --git a/lib/train.rb b/lib/train.rb index c85e90cf..a5ff6eaf 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -7,6 +7,7 @@ require 'train/plugins' require 'train/errors' require 'train/platforms' +require 'train/platforms/detect' require 'uri' module Train diff --git a/lib/train/extras/command_wrapper.rb b/lib/train/extras/command_wrapper.rb index ac49e966..62f8da20 100644 --- a/lib/train/extras/command_wrapper.rb +++ b/lib/train/extras/command_wrapper.rb @@ -162,7 +162,7 @@ class CommandWrapper include_options LinuxCommand def self.load(transport, options) - if transport.os.arch == 'unix' + if transport.os.type == 'unix' return nil unless LinuxCommand.active?(options) res = LinuxCommand.new(transport, options) msg = res.verify @@ -170,7 +170,7 @@ def self.load(transport, options) res # only use powershell command for local transport. winrm transport # uses powershell as default - elsif transport.os.windows? && transport.class == Train::Transports::Local::Connection + elsif transport.os.type == 'windows' && transport.class == Train::Transports::Local::Connection PowerShellCommand.new(transport, options) end end diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 56954e5a..f0664c95 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -78,13 +78,4 @@ def self.print_children(parent, pad = 2) print_children(obj, pad + 2) unless !defined?(obj.children) || obj.children.nil? end end - - require 'train/platforms/specs/os' - def self.detect(backend) - @backend = backend - instance_eval(&Train::Platforms.family('linux').detect) - Train::Platforms.name('ubuntu').backend = @backend - Train::Platforms.name('ubuntu').arch = 'unix' - Train::Platforms.name('ubuntu') - end end diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index 066977e1..02519e8f 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -1,10 +1,56 @@ # encoding: utf-8 -# -# Author:: Jared Quick -require 'train/platform/specs/linux' +require 'train/platforms/specs/os' +require 'train/platforms/detect/os_common' -module Train::Detect +module Train::Platforms::Detect + extend Train::Platforms::Detect::OSCommon + def self.scan(backend) + @backend = backend + @platform = {} + # Start at the top + top = Train::Platforms.top_platforms + + top.each do |name, plat| + puts "---> Testing: #{name} - #{plat.class}" + if plat.detect + result = instance_eval(&plat.detect) + # if we have a match start looking at the children + if result + child_result = scan_children(plat) + return child_result unless child_result.nil? + end + else + warn "#{name} will not be evaluated as the detect block is not set" if plat.detect.nil? + end + end + raise 'Sorry we did not find your platform' + end + + def self.scan_children(parent) + parent.children.each do |plat, condition| + if condition + # TODO: check condition vs @platform + end + unless plat.detect.nil? + puts "---> Testing: #{plat.name} - #{plat.class}" + result = instance_eval(&plat.detect) + if result && plat.class == Train::Platform + puts "FOUND One!!!" + plat.backend = @backend + # set all the info as part of the platform class + @platform.each { |name, value| plat.name = value unless name == 'name'} + return plat + else + child_result = scan_children(plat) unless plat.children.nil? + return child_result unless child_result.nil? + end + else + warn "#{plat.name} will not be evaluated as the detect block is not set" if plat.detect.nil? + end + end + false + end end diff --git a/lib/train/platforms/detect/linux_lsb.rb b/lib/train/platforms/detect/linux_lsb.rb new file mode 100644 index 00000000..042a5323 --- /dev/null +++ b/lib/train/platforms/detect/linux_lsb.rb @@ -0,0 +1,53 @@ +# encoding: utf-8 + +module Train::Platforms::Detect + module LinuxLSB + def lsb_config(content) + { + id: content[/^DISTRIB_ID=["']?(.+?)["']?$/, 1], + release: content[/^DISTRIB_RELEASE=["']?(.+?)["']?$/, 1], + codename: content[/^DISTRIB_CODENAME=["']?(.+?)["']?$/, 1], + } + end + + def lsb_release + raw = @backend.run_command('lsb_release -a').stdout + { + id: raw[/^Distributor ID:\s+(.+)$/, 1], + release: raw[/^Release:\s+(.+)$/, 1], + codename: raw[/^Codename:\s+(.+)$/, 1], + } + end + + def lsb + return @lsb if defined?(@lsb) + @lsb = {} + if !(raw = get_config('/etc/lsb-release')).nil? + @lsb = lsb_config(raw) + elsif unix_file?('/usr/bin/lsb_release') + @lsb = lsb_release + end + @lsb + end + + def detect_linux_via_lsb + lsb if @lsb.nil? + return false if @lsb[:id].nil? + id = @lsb[:id].downcase + case id + when /redhat/ + @platform[:family] = 'redhat' + when /amazon/ + @platform[:family] = 'amazon' + when /scientificsl/ + @platform[:family] = 'scientific' + when /xenserver/ + @platform[:family] = 'xenserver' + else + @platform[:family] = id + end + @platform[:release] = @lsb[:release] + true + end + end +end diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb new file mode 100644 index 00000000..175bb325 --- /dev/null +++ b/lib/train/platforms/detect/os_common.rb @@ -0,0 +1,76 @@ +# encoding: utf-8 + +require 'train/platforms/detect/os_linux' +require 'train/platforms/detect/linux_lsb' + +module Train::Platforms::Detect + module OSCommon + include Train::Platforms::Detect::Linux + include Train::Platforms::Detect::LinuxLSB + + def detect_family + # if some information is already defined, try to verify it + # with the remaining detection + unless @platform[:family].nil? + # return ok if the preconfigured family yielded a good result + return true if detect_family_type + # if not, reset the platform to presets and run the full detection + # TODO: print an error message in this case, as the instantiating + # backend is doing something wrong + @platform = {} + end + + # TODO: extend base implementation for detecting the family type + # to Windows and others + case uname_s + when /unrecognized command verb/ + @platform[:family] = 'openvms' + when /linux/i + @platform[:family] = 'linux' + when /./ + @platform[:family] = 'unix' + else + # Don't know what this is + @platform[:family] = nil + end + + # try to detect the platform if the platform is set to nil, otherwise this code will never work + return nil if @platform[:family].nil? + detect_family_type + end + + def detect_family_type # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + pf = @platform[:family] + + return detect_windows if pf == 'windows' + return detect_darwin if pf == 'darwin' + return detect_esx if pf == 'esx' + return detect_openvms if pf =='openvms' + + if %w{freebsd netbsd openbsd aix solaris2 hpux}.include?(pf) + return detect_via_uname + end + + # unix based systems combine the above + return true if pf == 'unix' and detect_darwin + return true if pf == 'unix' and detect_esx + # This is assuming that pf is set to unix, this should be if pf == 'linux' + return true if pf == 'unix' and detect_arista_eos + return true if pf == 'unix' and detect_via_uname + + # if we arrive here, we most likey have a regular linux + detect_linux + end + + def get_config(path) + res = @backend.run_command("test -f #{path} && cat #{path}") + # ignore files that can't be read + return nil if res.exit_status != 0 + res.stdout + end + + def unix_file?(path) + @backend.run_command("test -f #{path}").exit_status == 0 + end + end +end diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb new file mode 100644 index 00000000..8aa8a859 --- /dev/null +++ b/lib/train/platforms/detect/os_linux.rb @@ -0,0 +1,164 @@ +# encoding: utf-8 + +require 'train/platforms/detect/linux_lsb' +require 'train/platforms/detect/uname' + +module Train::Platforms::Detect + module Linux # rubocop:disable Metrics/ModuleLength + DEBIAN_FAMILY = %w{debian ubuntu linuxmint raspbian}.freeze + REDHAT_FAMILY = %w{centos redhat oracle scientific enterpriseenterprise xenserver cloudlinux ibm_powerkvm nexus_centos wrlinux virtuozzo parallels}.freeze + SUSE_FAMILY = %w{suse opensuse}.freeze + + include Train::Platforms::Detect::LinuxLSB + include Train::Platforms::Detect::Uname + + def detect_linux_via_config # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity +=begin + # MOVED to Redhat.oracle detect block + # if !(raw = get_config('/etc/oracle-release')).nil? + # @platform[:name] = 'oracle' + # @platform[:release] = redhatish_version(raw) + # elsif !(raw = get_config('/etc/enterprise-release')).nil? + # @platform[:name] = 'oracle' + # @platform[:release] = redhatish_version(raw) + + # MOVED to Debian detect block + # elsif !(raw = get_config('/etc/debian_version')).nil? + # case lsb[:id] + # when /ubuntu/i + # @platform[:name] = 'ubuntu' + # @platform[:release] = lsb[:release] + # when /linuxmint/i + # @platform[:name] = 'linuxmint' + # @platform[:release] = lsb[:release] + # else + # @platform[:name] = unix_file?('/usr/bin/raspi-config') ? 'raspbian' : 'debian' + # @platform[:release] = raw.chomp + # end + + # if !(raw = get_config('/etc/parallels-release')).nil? + # @platform[:name] = redhatish_platform(raw) + # @platform[:release] = raw[/(\d\.\d\.\d)/, 1] + # elsif !(raw = get_config('/etc/redhat-release')).nil? + # # TODO: Cisco + # # TODO: fully investigate os-release and integrate it; + # # here we just use it for centos + # @platform[:name] = if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i + # 'centos' + # else + # redhatish_platform(raw) + # end + # + # @platform[:release] = redhatish_version(raw) + # elsif !(raw = get_config('/etc/system-release')).nil? + # # Amazon Linux + # @platform[:name] = redhatish_platform(raw) + # @platform[:release] = redhatish_version(raw) + + elsif !(suse = get_config('/etc/SuSE-release')).nil? + version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') + version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' + @platform[:release] = version + @platform[:name] = if suse =~ /^openSUSE/ + 'opensuse' + else + 'suse' + end + elsif !(raw = get_config('/etc/arch-release')).nil? + @platform[:name] = 'arch' + # Because this is a rolling release distribution, + # use the kernel release, ex. 4.1.6-1-ARCH + @platform[:release] = uname_r + elsif !(raw = get_config('/etc/slackware-version')).nil? + @platform[:name] = 'slackware' + @platform[:release] = raw.scan(/(\d+|\.+)/).join + elsif !(raw = get_config('/etc/exherbo-release')).nil? + @platform[:name] = 'exherbo' + # Because this is a rolling release distribution, + # use the kernel release, ex. 4.1.6 + @platform[:release] = uname_r + elsif !(raw = get_config('/etc/gentoo-release')).nil? + @platform[:name] = 'gentoo' + @platform[:release] = raw.scan(/(\d+|\.+)/).join + elsif !(raw = get_config('/etc/alpine-release')).nil? + @platform[:name] = 'alpine' + @platform[:release] = raw.strip + elsif !get_config('/etc/coreos/update.conf').nil? + @platform[:name] = 'coreos' + @platform[:release] = lsb[:release] + elsif !(os_info = fetch_os_release).nil? + if os_info['ID_LIKE'] =~ /wrlinux/ + @platform[:name] = 'wrlinux' + @platform[:release] = os_info['VERSION'] + end + end +=end + + # @platform[:family] = family_for_platform + # + # !@platform[:family].nil? && !@platform[:release].nil? + true + end + + def family_for_platform + if DEBIAN_FAMILY.include?(@platform[:name]) + 'debian' + elsif REDHAT_FAMILY.include?(@platform[:name]) + 'redhat' + elsif SUSE_FAMILY.include?(@platform[:name]) + 'suse' + else + @platform[:name] || @platform[:family] + end + end + + def redhatish_platform(conf) + conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase + end + + def redhatish_version(conf) + return conf[/((\d+) \(Rawhide\))/i, 1].downcase if conf[/rawhide/i] + return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i] + conf[/release ([\d\.]+)/, 1] + end + + def detect_linux_arch + @platform[:arch] = uname_m + end + + def detect_linux + # TODO: print an error in this step of the detection + return false if uname_s.nil? || uname_s.empty? + return false if uname_r.nil? || uname_r.empty? + + detect_linux_arch + return true if detect_linux_via_config + return true if detect_linux_via_lsb + # in all other cases we failed the detection + @platform[:family] = 'unknown' + end + + def fetch_os_release + data = get_config('/etc/os-release') + return if data.nil? + + os_info = parse_os_release_info(data) + cisco_info_file = os_info['CISCO_RELEASE_INFO'] + if cisco_info_file + os_info.merge!(parse_os_release_info(get_config(cisco_info_file))) + end + + os_info + end + + def parse_os_release_info(raw) + return {} if raw.nil? + + raw.lines.each_with_object({}) do |line, memo| + line.strip! + key, value = line.split('=', 2) + memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty? + end + end + end +end diff --git a/lib/train/platforms/detect/uname.rb b/lib/train/platforms/detect/uname.rb new file mode 100644 index 00000000..41be2101 --- /dev/null +++ b/lib/train/platforms/detect/uname.rb @@ -0,0 +1,21 @@ +# encoding: utf-8 + +module Train::Platforms::Detect + module Uname + def uname_s + @uname_s ||= @backend.run_command('uname -s').stdout + end + + def uname_r + @uname_r ||= begin + res = @backend.run_command('uname -r').stdout + res.strip! unless res.nil? + res + end + end + + def uname_m + @uname_m ||= @backend.run_command('uname -m').stdout.chomp + end + end +end diff --git a/lib/train/platforms/specs/os.rb b/lib/train/platforms/specs/os.rb index 973dd66b..9d8c4119 100644 --- a/lib/train/platforms/specs/os.rb +++ b/lib/train/platforms/specs/os.rb @@ -1,22 +1,124 @@ -platform = Train::Platforms +plat = Train::Platforms + +# unix +plat.family('unix') + .detect { + true + } # linux -platform.family('linux') - .is_a('unix') - .detect { - puts @backend.run_command('uname -s').stdout - } +plat.family('linux').is_a('unix') + .detect { + detect_family + true if @platform[:family] == 'linux' + } # debian -platform.family('debian').is_a('linux') -platform.name('debian').title('Debian Linux').is_a('debian') -platform.name('ubuntu').is_a('debian').title('Ubuntu Linux') -platform.name('linuxmint').title('LinuxMint').is_a('debian').release('7.0') -platform.name('backtrack', release: '>= 4').is_a('debian') +plat.family('debian').is_a('linux') + .detect { + if !(raw = get_config('/etc/debian_version')).nil? + # load lsb info + lsb + true + end + } +plat.name('ubuntu').title('Ubuntu Linux').is_a('debian') + .detect { + if @lsb[:id] =~ /ubuntu/i + @platform[:name] = 'ubuntu' + @platform[:release] = lsb[:release] + true + end + } +plat.name('linuxmint').title('LinuxMint').is_a('debian') + .detect { + if @lsb[:id] =~ /ubuntu/i + @platform[:name] = 'linuxmint' + @platform[:release] = lsb[:release] + true + end + } +plat.name('raspbian').title('Raspbian Linux').is_a('debian') + .detect { + if unix_file?('/usr/bin/raspi-config') + @platform[:name] = 'raspbian' + @platform[:release] = raw.chomp + true + end + } +plat.name('backtrack', release: '>= 4').is_a('debian') +plat.name('debian').title('Debian Linux').is_a('debian') + .detect { + # Must be a debian + unless @lsb.nil? + @platform[:name] = 'debian' + @platform[:release] = lsb[:release] + true + end + } # redhat -platform.family('redhat').is_a('linux') -platform.name('centos').title('Centos Linux').is_a('redhat') -platform.name('rhel').title('Red Hat Enterprise Linux').is_a('redhat') -platform.name('oracle').title('Oracle Linux').is_a('redhat') -platform.name('awz').title('Amazon Linux').is_a('redhat') +plat.family('redhat').is_a('linux') + .detect { + if !(raw = get_config('/etc/redhat-release')).nil? + # TODO: Cisco + # TODO: fully investigate os-release and integrate it; + # here we just use it for centos + @platform[:name] = if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i + 'centos' + else + redhatish_platform(raw) + end + + @platform[:release] = redhatish_version(raw) + true + end + + # There is no easy way to detect the redhat family. This + # will detect all redhats at the platform level + true + } + +plat.name('centos').title('Centos Linux').is_a('redhat') + .detect { + true if @plaform[:name] == 'centos' + } +plat.name('rhel').title('Red Hat Enterplat.ise Linux').is_a('redhat') + .detect { + true if @plaform[:name] == 'rhel' + } +plat.name('oracle').title('Oracle Linux').is_a('redhat') + .detect { + if !(raw = get_config('/etc/oracle-release')).nil? + @platform[:name] = 'oracle' + @platform[:release] = redhatish_version(raw) + true + elsif !(raw = get_config('/etc/enterprise-release')).nil? + @platform[:name] = 'oracle' + @platform[:release] = redhatish_version(raw) + true + end + } +plat.name('amazon').title('Amazon Linux').is_a('redhat') + .detect { + if !(raw = get_config('/etc/system-release')).nil? + # Amazon Linux + @platform[:name] = redhatish_platform(raw) + @platform[:release] = redhatish_version(raw) + true + end + } +plat.name('parallels-release').title('Parallels Linux').is_a('redhat') + .detect { + if !(raw = get_config('/etc/parallels-release')).nil? + @platform[:name] = redhatish_platform(raw) + @platform[:release] = raw[/(\d\.\d\.\d)/, 1] + true + end + } + +# genaric linux +plat.name('linux').title('Genaric Linux').is_a('linux') + .detect { + true + } diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index 6b4edfe1..f9803266 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -62,7 +62,7 @@ def os end def platform - @platform ||= Train::Platforms.detect(self) + @platform ||= Train::Platforms::Detect.scan(self) end def file(path) From 922a716234c1690b544a477def7d8c7dca9e6bd6 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Tue, 17 Oct 2017 11:09:52 -0400 Subject: [PATCH 04/22] Move in linux detect logic Signed-off-by: Jared Quick --- lib/train/platforms/detect/os_common.rb | 59 +++++------- lib/train/platforms/detect/os_linux.rb | 108 ---------------------- lib/train/platforms/specs/os.rb | 117 ++++++++++++++++++++++-- 3 files changed, 132 insertions(+), 152 deletions(-) diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index 175bb325..fa302a2e 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -9,19 +9,6 @@ module OSCommon include Train::Platforms::Detect::LinuxLSB def detect_family - # if some information is already defined, try to verify it - # with the remaining detection - unless @platform[:family].nil? - # return ok if the preconfigured family yielded a good result - return true if detect_family_type - # if not, reset the platform to presets and run the full detection - # TODO: print an error message in this case, as the instantiating - # backend is doing something wrong - @platform = {} - end - - # TODO: extend base implementation for detecting the family type - # to Windows and others case uname_s when /unrecognized command verb/ @platform[:family] = 'openvms' @@ -34,33 +21,29 @@ def detect_family @platform[:family] = nil end - # try to detect the platform if the platform is set to nil, otherwise this code will never work - return nil if @platform[:family].nil? - detect_family_type end - def detect_family_type # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - pf = @platform[:family] - - return detect_windows if pf == 'windows' - return detect_darwin if pf == 'darwin' - return detect_esx if pf == 'esx' - return detect_openvms if pf =='openvms' - - if %w{freebsd netbsd openbsd aix solaris2 hpux}.include?(pf) - return detect_via_uname - end - - # unix based systems combine the above - return true if pf == 'unix' and detect_darwin - return true if pf == 'unix' and detect_esx - # This is assuming that pf is set to unix, this should be if pf == 'linux' - return true if pf == 'unix' and detect_arista_eos - return true if pf == 'unix' and detect_via_uname - - # if we arrive here, we most likey have a regular linux - detect_linux - end + # def detect_family_type # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + + # this will now happen on the detect blocks + # + # return detect_windows if pf == 'windows' + # return detect_darwin if pf == 'darwin' + # return detect_esx if pf == 'esx' + # return detect_openvms if pf =='openvms' + # + # if %w{freebsd netbsd openbsd aix solaris2 hpux}.include?(pf) + # return detect_via_uname + # end + # + # # unix based systems combine the above + # return true if pf == 'unix' and detect_darwin + # return true if pf == 'unix' and detect_esx + # # This is assuming that pf is set to unix, this should be if pf == 'linux' + # return true if pf == 'unix' and detect_arista_eos + # return true if pf == 'unix' and detect_via_uname + + # end def get_config(path) res = @backend.run_command("test -f #{path} && cat #{path}") diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb index 8aa8a859..e5872eaf 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/os_linux.rb @@ -5,113 +5,9 @@ module Train::Platforms::Detect module Linux # rubocop:disable Metrics/ModuleLength - DEBIAN_FAMILY = %w{debian ubuntu linuxmint raspbian}.freeze - REDHAT_FAMILY = %w{centos redhat oracle scientific enterpriseenterprise xenserver cloudlinux ibm_powerkvm nexus_centos wrlinux virtuozzo parallels}.freeze - SUSE_FAMILY = %w{suse opensuse}.freeze - include Train::Platforms::Detect::LinuxLSB include Train::Platforms::Detect::Uname - def detect_linux_via_config # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity -=begin - # MOVED to Redhat.oracle detect block - # if !(raw = get_config('/etc/oracle-release')).nil? - # @platform[:name] = 'oracle' - # @platform[:release] = redhatish_version(raw) - # elsif !(raw = get_config('/etc/enterprise-release')).nil? - # @platform[:name] = 'oracle' - # @platform[:release] = redhatish_version(raw) - - # MOVED to Debian detect block - # elsif !(raw = get_config('/etc/debian_version')).nil? - # case lsb[:id] - # when /ubuntu/i - # @platform[:name] = 'ubuntu' - # @platform[:release] = lsb[:release] - # when /linuxmint/i - # @platform[:name] = 'linuxmint' - # @platform[:release] = lsb[:release] - # else - # @platform[:name] = unix_file?('/usr/bin/raspi-config') ? 'raspbian' : 'debian' - # @platform[:release] = raw.chomp - # end - - # if !(raw = get_config('/etc/parallels-release')).nil? - # @platform[:name] = redhatish_platform(raw) - # @platform[:release] = raw[/(\d\.\d\.\d)/, 1] - # elsif !(raw = get_config('/etc/redhat-release')).nil? - # # TODO: Cisco - # # TODO: fully investigate os-release and integrate it; - # # here we just use it for centos - # @platform[:name] = if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i - # 'centos' - # else - # redhatish_platform(raw) - # end - # - # @platform[:release] = redhatish_version(raw) - # elsif !(raw = get_config('/etc/system-release')).nil? - # # Amazon Linux - # @platform[:name] = redhatish_platform(raw) - # @platform[:release] = redhatish_version(raw) - - elsif !(suse = get_config('/etc/SuSE-release')).nil? - version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') - version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' - @platform[:release] = version - @platform[:name] = if suse =~ /^openSUSE/ - 'opensuse' - else - 'suse' - end - elsif !(raw = get_config('/etc/arch-release')).nil? - @platform[:name] = 'arch' - # Because this is a rolling release distribution, - # use the kernel release, ex. 4.1.6-1-ARCH - @platform[:release] = uname_r - elsif !(raw = get_config('/etc/slackware-version')).nil? - @platform[:name] = 'slackware' - @platform[:release] = raw.scan(/(\d+|\.+)/).join - elsif !(raw = get_config('/etc/exherbo-release')).nil? - @platform[:name] = 'exherbo' - # Because this is a rolling release distribution, - # use the kernel release, ex. 4.1.6 - @platform[:release] = uname_r - elsif !(raw = get_config('/etc/gentoo-release')).nil? - @platform[:name] = 'gentoo' - @platform[:release] = raw.scan(/(\d+|\.+)/).join - elsif !(raw = get_config('/etc/alpine-release')).nil? - @platform[:name] = 'alpine' - @platform[:release] = raw.strip - elsif !get_config('/etc/coreos/update.conf').nil? - @platform[:name] = 'coreos' - @platform[:release] = lsb[:release] - elsif !(os_info = fetch_os_release).nil? - if os_info['ID_LIKE'] =~ /wrlinux/ - @platform[:name] = 'wrlinux' - @platform[:release] = os_info['VERSION'] - end - end -=end - - # @platform[:family] = family_for_platform - # - # !@platform[:family].nil? && !@platform[:release].nil? - true - end - - def family_for_platform - if DEBIAN_FAMILY.include?(@platform[:name]) - 'debian' - elsif REDHAT_FAMILY.include?(@platform[:name]) - 'redhat' - elsif SUSE_FAMILY.include?(@platform[:name]) - 'suse' - else - @platform[:name] || @platform[:family] - end - end - def redhatish_platform(conf) conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase end @@ -132,10 +28,6 @@ def detect_linux return false if uname_r.nil? || uname_r.empty? detect_linux_arch - return true if detect_linux_via_config - return true if detect_linux_via_lsb - # in all other cases we failed the detection - @platform[:family] = 'unknown' end def fetch_os_release diff --git a/lib/train/platforms/specs/os.rb b/lib/train/platforms/specs/os.rb index 9d8c4119..eefa4312 100644 --- a/lib/train/platforms/specs/os.rb +++ b/lib/train/platforms/specs/os.rb @@ -3,22 +3,26 @@ # unix plat.family('unix') .detect { - true + if @platform.empty? || @platform[:family] == 'unix' + detect_family + true + end } # linux plat.family('linux').is_a('unix') .detect { - detect_family - true if @platform[:family] == 'linux' + if @platform[:family] == 'linux' + detect_linux + detect_linux_via_lsb + true + end } # debian plat.family('debian').is_a('linux') .detect { if !(raw = get_config('/etc/debian_version')).nil? - # load lsb info - lsb true end } @@ -78,7 +82,6 @@ # will detect all redhats at the platform level true } - plat.name('centos').title('Centos Linux').is_a('redhat') .detect { true if @plaform[:name] == 'centos' @@ -116,6 +119,108 @@ true end } +plat.name('wrlinux').title('Wind River Linux').is_a('redhat') + .detect { + if !(os_info = fetch_os_release).nil? + if os_info['ID_LIKE'] =~ /wrlinux/ + @platform[:name] = 'wrlinux' + @platform[:release] = os_info['VERSION'] + true + end + end + } + +# suse +plat.family('suse').is_a('linux') + .detect { + if !(suse = get_config('/etc/SuSE-release')).nil? + version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') + version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' + @platform[:release] = version + @platform[:name] = if suse =~ /^openSUSE/ + 'opensuse' + else + 'suse' + end + true + end + } +plat.name('suse').title('Suse Linux').is_a('suse') + .detect { + true if @plaform[:name] == 'suse' + } +plat.name('opensuse').title('OpenSUSE Linux').is_a('suse') + .detect { + true if @plaform[:name] == 'opensuse' + } + +# arch +plat.family('arch').is_a('linux') + .detect { + if !get_config('/etc/arch-release').nil? + @platform[:name] = 'arch' + # Because this is a rolling release distribution, + # use the kernel release, ex. 4.1.6-1-ARCH + @platform[:release] = uname_r + true + end + } +plat.name('arch').title('Arch Linux').is_a('arch') + .detect { + true if @plaform[:name] == 'arch' + } + +# slackware +plat.name('slackware').title('Slackware Linux').is_a('linux') + .detect { + if !(raw = get_config('/etc/slackware-version')).nil? + @platform[:name] = 'slackware' + @platform[:release] = raw.scan(/(\d+|\.+)/).join + true + end + } + +# gentoo +plat.name('gentoo').title('Gentoo Linux').is_a('linux') + .detect { + if !(raw = get_config('/etc/gentoo-release')).nil? + @platform[:name] = 'gentoo' + @platform[:release] = raw.scan(/(\d+|\.+)/).join + true + end + } + +# exherbo +plat.name('exherbo').title('Exherbo Linux').is_a('linux') + .detect { + if !(raw = get_config('/etc/exherbo-release')).nil? + @platform[:name] = 'exherbo' + # Because this is a rolling release distribution, + # use the kernel release, ex. 4.1.6 + @platform[:release] = uname_r + true + end + } + +# alpine +plat.name('alpine').title('Alpine Linux').is_a('linux') + .detect { + if !(raw = get_config('/etc/alpine-release')).nil? + @platform[:name] = 'alpine' + @platform[:release] = raw.strip + true + end + } + +# coreos +plat.name('coreos').title('CoreOS Linux').is_a('linux') + .detect { + if !get_config('/etc/coreos/update.conf').nil? + @platform[:name] = 'coreos' + @platform[:release] = lsb[:release] + true + end + } # genaric linux plat.name('linux').title('Genaric Linux').is_a('linux') From f7567a785750007a58fde8135df79939921a0c11 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Tue, 17 Oct 2017 17:08:46 -0400 Subject: [PATCH 05/22] Add in local detect and more unix checks Signed-off-by: Jared Quick --- lib/train/platforms/detect.rb | 51 ++++++---- lib/train/platforms/detect/linux_lsb.rb | 10 +- lib/train/platforms/detect/os_common.rb | 3 + lib/train/platforms/detect/uname.rb | 9 +- lib/train/platforms/specs/os.rb | 123 +++++++++++++++++++++--- lib/train/transports/local.rb | 10 +- lib/train/transports/local_os.rb | 51 ---------- 7 files changed, 163 insertions(+), 94 deletions(-) delete mode 100644 lib/train/transports/local_os.rb diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index 02519e8f..c007ecf3 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -2,16 +2,32 @@ require 'train/platforms/specs/os' require 'train/platforms/detect/os_common' +require 'train/platforms/detect/os_local' module Train::Platforms::Detect extend Train::Platforms::Detect::OSCommon + extend Train::Platforms::Detect::OSLocal - def self.scan(backend) + def self.scan(backend, local = false) @backend = backend @platform = {} + # grab local platform info if we are running local + if local == true + @platform[:local] = true + detect_local_os + end + + # return the exact platform if we already know it + return get_platform if @platform[:name] + # Start at the top - top = Train::Platforms.top_platforms + if @platform[:family] + # go to the exact family if we already know it + top = [Train::Platforms.list[@platform[:family]]] + else + top = Train::Platforms.top_platforms + end top.each do |name, plat| puts "---> Testing: #{name} - #{plat.class}" @@ -22,8 +38,6 @@ def self.scan(backend) child_result = scan_children(plat) return child_result unless child_result.nil? end - else - warn "#{name} will not be evaluated as the detect block is not set" if plat.detect.nil? end end raise 'Sorry we did not find your platform' @@ -32,25 +46,26 @@ def self.scan(backend) def self.scan_children(parent) parent.children.each do |plat, condition| if condition - # TODO: check condition vs @platform + condition.each do |k, v| + return if @platform[k] != v + end end unless plat.detect.nil? puts "---> Testing: #{plat.name} - #{plat.class}" result = instance_eval(&plat.detect) - if result && plat.class == Train::Platform - puts "FOUND One!!!" - plat.backend = @backend - # set all the info as part of the platform class - @platform.each { |name, value| plat.name = value unless name == 'name'} - return plat - else - child_result = scan_children(plat) unless plat.children.nil? - return child_result unless child_result.nil? - end - else - warn "#{plat.name} will not be evaluated as the detect block is not set" if plat.detect.nil? + return get_platform if result && @platform[:name] + child_result = scan_children(plat) unless plat.children.nil? + return child_result unless child_result.nil? end end - false + nil + end + + def self.get_platform + plat = Train::Platforms.list[@platform[:name]] + plat.backend = @backend + # this will just add all the platform info to the platform object as instance variables + @platform.each { |name, value| plat.name = value unless name == 'name' } + plat unless plat.nil? end end diff --git a/lib/train/platforms/detect/linux_lsb.rb b/lib/train/platforms/detect/linux_lsb.rb index 042a5323..1c76718d 100644 --- a/lib/train/platforms/detect/linux_lsb.rb +++ b/lib/train/platforms/detect/linux_lsb.rb @@ -36,15 +36,15 @@ def detect_linux_via_lsb id = @lsb[:id].downcase case id when /redhat/ - @platform[:family] = 'redhat' + @platform[:name] = 'redhat' when /amazon/ - @platform[:family] = 'amazon' + @platform[:name] = 'amazon' when /scientificsl/ - @platform[:family] = 'scientific' + @platform[:name] = 'scientific' when /xenserver/ - @platform[:family] = 'xenserver' + @platform[:name] = 'xenserver' else - @platform[:family] = id + @platform[:name] = id end @platform[:release] = @lsb[:release] true diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index fa302a2e..70a4adbd 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -21,6 +21,9 @@ def detect_family @platform[:family] = nil end + detect_via_uname if @platform[:family] == 'unix' + + end # def detect_family_type # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity diff --git a/lib/train/platforms/detect/uname.rb b/lib/train/platforms/detect/uname.rb index 41be2101..bc05b29b 100644 --- a/lib/train/platforms/detect/uname.rb +++ b/lib/train/platforms/detect/uname.rb @@ -3,11 +3,13 @@ module Train::Platforms::Detect module Uname def uname_s - @uname_s ||= @backend.run_command('uname -s').stdout + return @uname_s unless @uname_s.nil? + @uname_s = @backend.run_command('uname -s').stdout end def uname_r - @uname_r ||= begin + return @uname_r unless @uname_r.nil? + @uname_r = begin res = @backend.run_command('uname -r').stdout res.strip! unless res.nil? res @@ -15,7 +17,8 @@ def uname_r end def uname_m - @uname_m ||= @backend.run_command('uname -m').stdout.chomp + return @uname_m unless @uname_m.nil? + @uname_m = @backend.run_command('uname -m').stdout.chomp end end end diff --git a/lib/train/platforms/specs/os.rb b/lib/train/platforms/specs/os.rb index eefa4312..a9a1fe6e 100644 --- a/lib/train/platforms/specs/os.rb +++ b/lib/train/platforms/specs/os.rb @@ -1,15 +1,20 @@ plat = Train::Platforms -# unix +# windows platform +plat.name('windows') + .detect { + return true if @platform[:name] =~ /windows|microsoft/i + } + +# unix master family plat.family('unix') .detect { - if @platform.empty? || @platform[:family] == 'unix' - detect_family - true - end + # must be unix. keep this at the end + detect_family + true } -# linux +# linux master family plat.family('linux').is_a('unix') .detect { if @platform[:family] == 'linux' @@ -19,7 +24,7 @@ end } -# debian +# debian family plat.family('debian').is_a('linux') .detect { if !(raw = get_config('/etc/debian_version')).nil? @@ -61,7 +66,7 @@ end } -# redhat +# redhat family plat.family('redhat').is_a('linux') .detect { if !(raw = get_config('/etc/redhat-release')).nil? @@ -82,13 +87,13 @@ # will detect all redhats at the platform level true } -plat.name('centos').title('Centos Linux').is_a('redhat') +plat.name('redhat').title('Red Hat Enterplat.ise Linux').is_a('redhat') .detect { - true if @plaform[:name] == 'centos' + # for now this is detected at the start during the lsb/family call } -plat.name('rhel').title('Red Hat Enterplat.ise Linux').is_a('redhat') +plat.name('centos').title('Centos Linux').is_a('redhat') .detect { - true if @plaform[:name] == 'rhel' + # for now this is detected at the start during the lsb/family call } plat.name('oracle').title('Oracle Linux').is_a('redhat') .detect { @@ -111,6 +116,14 @@ true end } +plat.name('scientific').title('Scientific Linux').is_a('redhat') + .detect { + # for now this is detected at the start during the lsb call + } +plat.name('xenserver').title('Xenserer Linux').is_a('redhat') + .detect { + # for now this is detected at the start during the lsb call + } plat.name('parallels-release').title('Parallels Linux').is_a('redhat') .detect { if !(raw = get_config('/etc/parallels-release')).nil? @@ -130,7 +143,7 @@ end } -# suse +# suse family plat.family('suse').is_a('linux') .detect { if !(suse = get_config('/etc/SuSE-release')).nil? @@ -147,11 +160,60 @@ } plat.name('suse').title('Suse Linux').is_a('suse') .detect { - true if @plaform[:name] == 'suse' } plat.name('opensuse').title('OpenSUSE Linux').is_a('suse') .detect { - true if @plaform[:name] == 'opensuse' + } + +# bsd family +plat.family('bsd') + .detect { + # we need a better way to determin this family + true + } +plat.name('darwin').title('Darwin').is_a('bsd') + .detect { + if uname_s =~ /darwin/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } +plat.name('freebsd').title('Freebsd').is_a('bsd') + .detect { + if uname_s =~ /freebsd/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } +plat.name('openbsd').title('Openbsd').is_a('bsd') + .detect { + if uname_s =~ /openbsd/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } +plat.name('netbsd').title('Netbsd').is_a('bsd') + .detect { + if uname_s =~ /netbsd/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } + +# solaris family +plat.family('solaris') + .detect { + # we need a better way to determin this family + true + } +# solaris (local only as of now) +plat.name('solaris').title('Solaris').is_a('solaris') + .detect { + true if @platform[:name] =~ /solaris/ } # arch @@ -222,6 +284,37 @@ end } +# aix +plat.name('aix').title('Aix').is_a('linux') + .detect { + if uname_s =~ /aix/ + @platform[:name] = uname_s.lines[0].chomp + out = @backend.run_command('uname -rvp').stdout + m = out.match(/(\d+)\s+(\d+)\s+(.*)/) + unless m.nil? + @platform[:release] = "#{m[2]}.#{m[1]}" + @platform[:arch] = m[3].to_s + end + true + end + } + +# hpux +plat.name('hpux').title('Hpux').is_a('linux') + .detect { + if uname_s =~ /hp-ux/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } + +# solaris (local only as of now) +plat.name('solaris').title('Solaris').is_a('solaris') + .detect { + true if @platform[:name] =~ /solaris/ + } + # genaric linux plat.name('linux').title('Genaric Linux').is_a('linux') .detect { diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index caf068a0..79317cf0 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -18,7 +18,6 @@ def connection(_ = nil) class Connection < BaseConnection require 'train/transports/local_file' - require 'train/transports/local_os' def initialize(options) super(options) @@ -36,9 +35,16 @@ def run_command(cmd) end def os - @os ||= OS.new(self) + warn '[DEPRECATION] `os` is being deprecated. ' \ + 'Please use `platform` instead.' + platform end + def platform + @platform ||= Train::Platforms::Detect.scan(self, true) + end + + def file(path) @files[path] ||= File.new(self, path) end diff --git a/lib/train/transports/local_os.rb b/lib/train/transports/local_os.rb deleted file mode 100644 index 72bc5088..00000000 --- a/lib/train/transports/local_os.rb +++ /dev/null @@ -1,51 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# - -require 'rbconfig' - -class Train::Transports::Local - class OS < OSCommon - def initialize(backend) - super(backend, { family: detect_local_os }) - end - - private - - def detect_local_os - case ::RbConfig::CONFIG['host_os'] - when /aix(.+)$/ - return 'aix' - when /darwin(.+)$/ - return 'darwin' - when /hpux(.+)$/ - return 'hpux' - when /linux/ - return 'linux' - when /freebsd(.+)$/ - return 'freebsd' - when /openbsd(.+)$/ - return 'openbsd' - when /netbsd(.*)$/ - return 'netbsd' - when /solaris2/ - return 'solaris2' - when /mswin|mingw32|windows/ - # After long discussion in IRC the "powers that be" have come to a consensus - # that no Windows platform exists that was not based on the - # Windows_NT kernel, so we herby decree that "windows" will refer to all - # platforms built upon the Windows_NT kernel and have access to win32 or win64 - # subsystems. - return 'windows' - else - return ::RbConfig::CONFIG['host_os'] - end - end - end -end From d296abc380e9d2b3fe976db9100200fed0dae44f Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Thu, 19 Oct 2017 11:54:36 -0400 Subject: [PATCH 06/22] Refactor os specifications and some cleaning Signed-off-by: Jared Quick --- lib/train.rb | 5 +- lib/train/exceptions.rb | 5 + lib/train/extras/command_wrapper.rb | 4 +- lib/train/platform.rb | 14 +- lib/train/platforms.rb | 2 - lib/train/platforms/common.rb | 16 +- lib/train/platforms/detect.rb | 42 ++- lib/train/platforms/detect/linux_lsb.rb | 19 +- lib/train/platforms/detect/os_local.rb | 38 +++ lib/train/platforms/family.rb | 2 - .../platforms/{specs => specifications}/os.rb | 265 +++++++++--------- lib/train/plugins/base_connection.rb | 6 + lib/train/transports/local.rb | 8 +- lib/train/transports/ssh_connection.rb | 2 - 14 files changed, 221 insertions(+), 207 deletions(-) create mode 100644 lib/train/exceptions.rb create mode 100644 lib/train/platforms/detect/os_local.rb rename lib/train/platforms/{specs => specifications}/os.rb (56%) diff --git a/lib/train.rb b/lib/train.rb index a5ff6eaf..52157761 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -7,7 +7,7 @@ require 'train/plugins' require 'train/errors' require 'train/platforms' -require 'train/platforms/detect' +require 'train/exceptions' require 'uri' module Train @@ -18,6 +18,9 @@ module Train # @param [Array] *args list of arguments for the plugin # @return [Transport] instance of the new transport or nil def self.create(name, *args) + # require built in detect platforms at this level so any manual ones will be at the top + require 'train/platforms/detect' + cls = load_transport(name) cls.new(*args) unless cls.nil? end diff --git a/lib/train/exceptions.rb b/lib/train/exceptions.rb new file mode 100644 index 00000000..72ecb480 --- /dev/null +++ b/lib/train/exceptions.rb @@ -0,0 +1,5 @@ +# encoding: utf-8 + +module Train::Exceptions + class PlatformDetectionFailed < StandardError; end +end diff --git a/lib/train/extras/command_wrapper.rb b/lib/train/extras/command_wrapper.rb index 62f8da20..716d1796 100644 --- a/lib/train/extras/command_wrapper.rb +++ b/lib/train/extras/command_wrapper.rb @@ -162,7 +162,7 @@ class CommandWrapper include_options LinuxCommand def self.load(transport, options) - if transport.os.type == 'unix' + if transport.os.unix? return nil unless LinuxCommand.active?(options) res = LinuxCommand.new(transport, options) msg = res.verify @@ -170,7 +170,7 @@ def self.load(transport, options) res # only use powershell command for local transport. winrm transport # uses powershell as default - elsif transport.os.type == 'windows' && transport.class == Train::Transports::Local::Connection + elsif transport.os.windows? && transport.class == Train::Transports::Local::Connection PowerShellCommand.new(transport, options) end end diff --git a/lib/train/platform.rb b/lib/train/platform.rb index 0abfd811..08d837c5 100644 --- a/lib/train/platform.rb +++ b/lib/train/platform.rb @@ -1,11 +1,9 @@ # encoding: utf-8 -# -# Author:: Jared Quick module Train class Platform include Train::Platforms::Common - attr_accessor :name, :condition, :families + attr_accessor :name, :condition, :families, :backend, :platform def initialize(name, condition = {}) @condition = condition @@ -16,6 +14,16 @@ def initialize(name, condition = {}) Train::Platforms.list[name] = self end + %w{unix? windows?}.each do |m| + define_method m do |_arg = nil| + @platform[:type] == m + end + end + + define_method "#{name}?" do |_arg = nil| + true + end + def title(title = nil) if @title.nil? && title.nil? name.capitalize diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index f0664c95..7e9047fd 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -1,6 +1,4 @@ # encoding: utf-8 -# -# Author:: Jared Quick require 'train/platforms/common' require 'train/platforms/family' diff --git a/lib/train/platforms/common.rb b/lib/train/platforms/common.rb index 778c359c..dc0fc06f 100644 --- a/lib/train/platforms/common.rb +++ b/lib/train/platforms/common.rb @@ -7,9 +7,9 @@ module Common # Add a family connection. This will create a family # if it does not exist and add a child relationship. - def is_a(family) + def in_family(family) if self.class == Train::Platforms::Family && @name == family - raise "Sorry you can not add a family inside itself '#{@name}.is_a(#{family})'" + raise "Sorry you can not add a family inside itself '#{@name}.in_family(#{family})'" end # add family to the family list @@ -22,19 +22,9 @@ def is_a(family) end def detect(&block) - return @detect if block.nil? + return @detect unless block_given? @detect = block self end - - def method_missing(m, *args, &block) - unless args.empty? - args = args.first if args.size == 1 - instance_variable_set("@#{m.to_s.chomp('=')}", args) - self - else - instance_variable_get("@#{m}") - end - end end end diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index c007ecf3..fe3dd34e 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -1,6 +1,6 @@ # encoding: utf-8 -require 'train/platforms/specs/os' +require 'train/platforms/specifications/os' require 'train/platforms/detect/os_common' require 'train/platforms/detect/os_local' @@ -8,29 +8,20 @@ module Train::Platforms::Detect extend Train::Platforms::Detect::OSCommon extend Train::Platforms::Detect::OSLocal - def self.scan(backend, local = false) + def self.scan(backend) @backend = backend @platform = {} # grab local platform info if we are running local - if local == true - @platform[:local] = true - detect_local_os + if @backend.local? + plat = detect_local_os + return get_platform(plat) if plat end - # return the exact platform if we already know it - return get_platform if @platform[:name] - # Start at the top - if @platform[:family] - # go to the exact family if we already know it - top = [Train::Platforms.list[@platform[:family]]] - else - top = Train::Platforms.top_platforms - end - + top = Train::Platforms.top_platforms top.each do |name, plat| - puts "---> Testing: #{name} - #{plat.class}" + puts "---> Testing-TOP: #{name} - #{plat.class}" if plat.detect result = instance_eval(&plat.detect) # if we have a match start looking at the children @@ -40,6 +31,8 @@ def self.scan(backend, local = false) end end end + + # raise Train::Exceptions::PlatformDetectionFailed 'Sorry we did not find your platform' raise 'Sorry we did not find your platform' end @@ -47,25 +40,26 @@ def self.scan_children(parent) parent.children.each do |plat, condition| if condition condition.each do |k, v| - return if @platform[k] != v + return nil if @platform[k] != v end end unless plat.detect.nil? puts "---> Testing: #{plat.name} - #{plat.class}" result = instance_eval(&plat.detect) - return get_platform if result && @platform[:name] - child_result = scan_children(plat) unless plat.children.nil? + return get_platform(plat) if result == true && plat.class == Train::Platform + child_result = scan_children(plat) if plat.respond_to?(:children) && !plat.children.nil? return child_result unless child_result.nil? end end nil end - def self.get_platform - plat = Train::Platforms.list[@platform[:name]] + def self.get_platform(plat) plat.backend = @backend - # this will just add all the platform info to the platform object as instance variables - @platform.each { |name, value| plat.name = value unless name == 'name' } - plat unless plat.nil? + plat.platform = @platform + puts "---" + puts plat.name + puts plat.platform.inspect + plat end end diff --git a/lib/train/platforms/detect/linux_lsb.rb b/lib/train/platforms/detect/linux_lsb.rb index 1c76718d..abc85ca0 100644 --- a/lib/train/platforms/detect/linux_lsb.rb +++ b/lib/train/platforms/detect/linux_lsb.rb @@ -30,24 +30,9 @@ def lsb @lsb end - def detect_linux_via_lsb + def detect_linux_lsb lsb if @lsb.nil? - return false if @lsb[:id].nil? - id = @lsb[:id].downcase - case id - when /redhat/ - @platform[:name] = 'redhat' - when /amazon/ - @platform[:name] = 'amazon' - when /scientificsl/ - @platform[:name] = 'scientific' - when /xenserver/ - @platform[:name] = 'xenserver' - else - @platform[:name] = id - end - @platform[:release] = @lsb[:release] - true + return @lsb unless @lsb[:id].nil? end end end diff --git a/lib/train/platforms/detect/os_local.rb b/lib/train/platforms/detect/os_local.rb new file mode 100644 index 00000000..2e0591ab --- /dev/null +++ b/lib/train/platforms/detect/os_local.rb @@ -0,0 +1,38 @@ +# encoding: utf-8 + +require 'rbconfig' + +module Train::Platforms::Detect + module OSLocal + def detect_local_os + case ::RbConfig::CONFIG['host_os'] + when /aix(.+)$/ + @platform[:name] = 'aix' + when /darwin(.+)$/ + @platform[:name] = 'darwin' + when /hpux(.+)$/ + @platform[:name] = 'hpux' + when /linux/ + @platform[:name] = 'linux' + when /freebsd(.+)$/ + @platform[:name] = 'freebsd' + when /openbsd(.+)$/ + @platform[:name] = 'openbsd' + when /netbsd(.*)$/ + @platform[:name] = 'netbsd' + when /solaris2/ + @platform[:name] = 'solaris2' + when /mswin|mingw32|windows/ + # After long discussion in IRC the "powers that be" have come to a consensus + # that no Windows platform exists that was not based on the + # Windows_NT kernel, so we herby decree that "windows" will refer to all + # platforms built upon the Windows_NT kernel and have access to win32 or win64 + # subsystems. + @platform[:name] = 'windows' + else + @platform[:name] = ::RbConfig::CONFIG['host_os'] + end + Train::Platforms.list[@platform[:name]] if @platform[:name] + end + end +end diff --git a/lib/train/platforms/family.rb b/lib/train/platforms/family.rb index f6dc5d7a..7735577d 100644 --- a/lib/train/platforms/family.rb +++ b/lib/train/platforms/family.rb @@ -1,6 +1,4 @@ # encoding: utf-8 -# -# Author:: Jared Quick module Train::Platforms class Family diff --git a/lib/train/platforms/specs/os.rb b/lib/train/platforms/specifications/os.rb similarity index 56% rename from lib/train/platforms/specs/os.rb rename to lib/train/platforms/specifications/os.rb index a9a1fe6e..101f6f74 100644 --- a/lib/train/platforms/specs/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -9,93 +9,92 @@ # unix master family plat.family('unix') .detect { - # must be unix. keep this at the end - detect_family - true + if uname_s =~ /linux/i + @platform[:family] = 'linux' + @platform[:type] = 'unix' + true + elsif uname_s =~ /./ + @platform[:type] = 'unix' + true + end } # linux master family -plat.family('linux').is_a('unix') +plat.family('linux').in_family('unix') .detect { if @platform[:family] == 'linux' - detect_linux - detect_linux_via_lsb + detect_linux_arch # sets arch in @platform[:arch] true end } # debian family -plat.family('debian').is_a('linux') +plat.family('debian').in_family('linux') .detect { - if !(raw = get_config('/etc/debian_version')).nil? - true - end + true unless get_config('/etc/debian_version').nil? } -plat.name('ubuntu').title('Ubuntu Linux').is_a('debian') +plat.name('debian').title('Debian Linux').in_family('debian') .detect { - if @lsb[:id] =~ /ubuntu/i - @platform[:name] = 'ubuntu' + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /debian/i @platform[:release] = lsb[:release] true end } -plat.name('linuxmint').title('LinuxMint').is_a('debian') +plat.name('ubuntu').title('Ubuntu Linux').in_family('debian') .detect { - if @lsb[:id] =~ /ubuntu/i - @platform[:name] = 'linuxmint' + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /ubuntu/i @platform[:release] = lsb[:release] true end } -plat.name('raspbian').title('Raspbian Linux').is_a('debian') +plat.name('linuxmint').title('LinuxMint').in_family('debian') .detect { - if unix_file?('/usr/bin/raspi-config') - @platform[:name] = 'raspbian' - @platform[:release] = raw.chomp + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /linuxmint/i + @platform[:release] = lsb[:release] true end } -plat.name('backtrack', release: '>= 4').is_a('debian') -plat.name('debian').title('Debian Linux').is_a('debian') +plat.name('raspbian').title('Raspbian Linux').in_family('debian') .detect { - # Must be a debian - unless @lsb.nil? - @platform[:name] = 'debian' - @platform[:release] = lsb[:release] + if unix_file?('/usr/bin/raspi-config') + @platform[:release] = get_config('/etc/debian_version').chomp true end } +plat.name('backtrack', release: '>= 4').in_family('debian') # redhat family -plat.family('redhat').is_a('linux') - .detect { - if !(raw = get_config('/etc/redhat-release')).nil? - # TODO: Cisco - # TODO: fully investigate os-release and integrate it; - # here we just use it for centos - @platform[:name] = if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i - 'centos' - else - redhatish_platform(raw) - end - - @platform[:release] = redhatish_version(raw) - true - end +plat.family('redhat').in_family('linux') + .detect { + # I am not sure this returns true for all redhats in this family + # for now we are going to just try each platform + # return true unless get_config('/etc/redhat-release').nil? - # There is no easy way to detect the redhat family. This - # will detect all redhats at the platform level true } -plat.name('redhat').title('Red Hat Enterplat.ise Linux').is_a('redhat') +plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') .detect { - # for now this is detected at the start during the lsb/family call + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /redhat/i + @platform[:release] = lsb[:release] + true + end } -plat.name('centos').title('Centos Linux').is_a('redhat') +plat.name('centos').title('Centos Linux').in_family('redhat') .detect { - # for now this is detected at the start during the lsb/family call + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /centos/i + @platform[:release] = lsb[:release] + true + elsif get_config('/etc/os-release') =~ /centos/i + @platform[:release] = redhatish_version(raw) + true + end } -plat.name('oracle').title('Oracle Linux').is_a('redhat') +plat.name('oracle').title('Oracle Linux').in_family('redhat') .detect { if !(raw = get_config('/etc/oracle-release')).nil? @platform[:name] = 'oracle' @@ -107,24 +106,29 @@ true end } -plat.name('amazon').title('Amazon Linux').is_a('redhat') +plat.name('amazon').title('Amazon Linux').in_family('redhat') .detect { - if !(raw = get_config('/etc/system-release')).nil? + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /amazon/i + @platform[:name] = 'amazon' + @platform[:release] = lsb[:release] + true + elsif !(raw = get_config('/etc/system-release')).nil? # Amazon Linux @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) true end } -plat.name('scientific').title('Scientific Linux').is_a('redhat') +plat.name('scientific').title('Scientific Linux').in_family('redhat') .detect { # for now this is detected at the start during the lsb call } -plat.name('xenserver').title('Xenserer Linux').is_a('redhat') +plat.name('xenserver').title('Xenserer Linux').in_family('redhat') .detect { # for now this is detected at the start during the lsb call } -plat.name('parallels-release').title('Parallels Linux').is_a('redhat') +plat.name('parallels-release').title('Parallels Linux').in_family('redhat') .detect { if !(raw = get_config('/etc/parallels-release')).nil? @platform[:name] = redhatish_platform(raw) @@ -132,7 +136,7 @@ true end } -plat.name('wrlinux').title('Wind River Linux').is_a('redhat') +plat.name('wrlinux').title('Wind River Linux').in_family('redhat') .detect { if !(os_info = fetch_os_release).nil? if os_info['ID_LIKE'] =~ /wrlinux/ @@ -144,119 +148,61 @@ } # suse family -plat.family('suse').is_a('linux') +plat.family('suse').in_family('linux') .detect { if !(suse = get_config('/etc/SuSE-release')).nil? version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' @platform[:release] = version - @platform[:name] = if suse =~ /^openSUSE/ - 'opensuse' - else - 'suse' - end true end } -plat.name('suse').title('Suse Linux').is_a('suse') +plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') .detect { + return true if get_config('/etc/SuSE-release') =~ /^openSUSE/ + # release set at the family level } -plat.name('opensuse').title('OpenSUSE Linux').is_a('suse') +plat.name('suse').title('Suse Linux').in_family('suse') .detect { + return true if get_config('/etc/SuSE-release') =~ /suse/ + # release set at the family level } -# bsd family -plat.family('bsd') - .detect { - # we need a better way to determin this family - true - } -plat.name('darwin').title('Darwin').is_a('bsd') - .detect { - if uname_s =~ /darwin/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - true - end - } -plat.name('freebsd').title('Freebsd').is_a('bsd') - .detect { - if uname_s =~ /freebsd/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - true - end - } -plat.name('openbsd').title('Openbsd').is_a('bsd') - .detect { - if uname_s =~ /openbsd/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - true - end - } -plat.name('netbsd').title('Netbsd').is_a('bsd') - .detect { - if uname_s =~ /netbsd/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - true - end - } -# solaris family -plat.family('solaris') - .detect { - # we need a better way to determin this family - true - } -# solaris (local only as of now) -plat.name('solaris').title('Solaris').is_a('solaris') - .detect { - true if @platform[:name] =~ /solaris/ - } # arch -plat.family('arch').is_a('linux') +plat.name('arch').title('Arch Linux').in_family('linux') .detect { if !get_config('/etc/arch-release').nil? - @platform[:name] = 'arch' # Because this is a rolling release distribution, # use the kernel release, ex. 4.1.6-1-ARCH @platform[:release] = uname_r true end } -plat.name('arch').title('Arch Linux').is_a('arch') - .detect { - true if @plaform[:name] == 'arch' - } # slackware -plat.name('slackware').title('Slackware Linux').is_a('linux') +plat.name('slackware').title('Slackware Linux').in_family('linux') .detect { if !(raw = get_config('/etc/slackware-version')).nil? - @platform[:name] = 'slackware' @platform[:release] = raw.scan(/(\d+|\.+)/).join true end } # gentoo -plat.name('gentoo').title('Gentoo Linux').is_a('linux') +plat.name('gentoo').title('Gentoo Linux').in_family('linux') .detect { if !(raw = get_config('/etc/gentoo-release')).nil? - @platform[:name] = 'gentoo' @platform[:release] = raw.scan(/(\d+|\.+)/).join true end } # exherbo -plat.name('exherbo').title('Exherbo Linux').is_a('linux') +plat.name('exherbo').title('Exherbo Linux').in_family('linux') .detect { if !(raw = get_config('/etc/exherbo-release')).nil? - @platform[:name] = 'exherbo' # Because this is a rolling release distribution, # use the kernel release, ex. 4.1.6 @platform[:release] = uname_r @@ -265,30 +211,33 @@ } # alpine -plat.name('alpine').title('Alpine Linux').is_a('linux') +plat.name('alpine').title('Alpine Linux').in_family('linux') .detect { if !(raw = get_config('/etc/alpine-release')).nil? - @platform[:name] = 'alpine' @platform[:release] = raw.strip true end } # coreos -plat.name('coreos').title('CoreOS Linux').is_a('linux') +plat.name('coreos').title('CoreOS Linux').in_family('linux') .detect { if !get_config('/etc/coreos/update.conf').nil? - @platform[:name] = 'coreos' @platform[:release] = lsb[:release] true end } +# genaric linux +plat.name('linux').title('Genaric Linux').in_family('linux') + .detect { + true + } + # aix -plat.name('aix').title('Aix').is_a('linux') +plat.name('aix').title('Aix').in_family('unix') .detect { if uname_s =~ /aix/ - @platform[:name] = uname_s.lines[0].chomp out = @backend.run_command('uname -rvp').stdout m = out.match(/(\d+)\s+(\d+)\s+(.*)/) unless m.nil? @@ -299,24 +248,64 @@ end } +# solaris family +plat.family('solaris').in_family('unix') + .detect { + # we need a better way to determin this family + true + } + +# solaris +plat.name('solaris').title('Solaris').in_family('solaris') + .detect { + # TODO: REDO + true if @platform[:name] =~ /solaris/ + } + # hpux -plat.name('hpux').title('Hpux').is_a('linux') +plat.name('hpux').title('Hpux').in_family('unix') .detect { if uname_s =~ /hp-ux/ - @platform[:name] = uname_s.lines[0].chomp @platform[:release] = uname_r.lines[0].chomp true end } -# solaris (local only as of now) -plat.name('solaris').title('Solaris').is_a('solaris') +# bsd family +plat.family('bsd').in_family('unix') .detect { - true if @platform[:name] =~ /solaris/ + # we need a better way to determin this family + true } - -# genaric linux -plat.name('linux').title('Genaric Linux').is_a('linux') +plat.name('darwin').title('Darwin').in_family('bsd') .detect { - true + if uname_s =~ /darwin/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } +plat.name('freebsd').title('Freebsd').in_family('bsd') + .detect { + if uname_s =~ /freebsd/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } +plat.name('openbsd').title('Openbsd').in_family('bsd') + .detect { + if uname_s =~ /openbsd/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } +plat.name('netbsd').title('Netbsd').in_family('bsd') + .detect { + if uname_s =~ /netbsd/ + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end } diff --git a/lib/train/plugins/base_connection.rb b/lib/train/plugins/base_connection.rb index fe246d49..05c9dc32 100644 --- a/lib/train/plugins/base_connection.rb +++ b/lib/train/plugins/base_connection.rb @@ -28,6 +28,7 @@ class BaseConnection def initialize(options = nil) @options = options || {} @logger = @options.delete(:logger) || Logger.new(STDOUT) + @logger.level = Logger::WARN @files = {} end @@ -49,6 +50,11 @@ def load_json(j) end end + # Is this a local transport? + def local? + false + end + # Execute a command using this connection. # # @param command [String] command string to execute diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index 79317cf0..4577d9f5 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -34,14 +34,16 @@ def run_command(cmd) CommandResult.new('', '', 1) end + def local? + true + end + def os - warn '[DEPRECATION] `os` is being deprecated. ' \ - 'Please use `platform` instead.' platform end def platform - @platform ||= Train::Platforms::Detect.scan(self, true) + @platform ||= Train::Platforms::Detect.scan(self) end diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index f9803266..5e005675 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -56,8 +56,6 @@ def close end def os - warn '[DEPRECATION] `os` is being deprecated. ' \ - 'Please use `platform` instead.' platform end From c7ae1a292299d32e92381760d79209a99e46112f Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Fri, 20 Oct 2017 15:51:59 -0400 Subject: [PATCH 07/22] Added windows and finalize detect Signed-off-by: Jared Quick --- lib/train/platforms.rb | 4 +- lib/train/platforms/detect.rb | 10 +- lib/train/platforms/detect/os_common.rb | 48 ++---- lib/train/platforms/detect/os_linux.rb | 12 -- lib/train/platforms/detect/os_windows.rb | 80 +++++++++ lib/train/platforms/specifications/os.rb | 198 ++++++++++++++++++----- lib/train/transports/docker.rb | 6 +- 7 files changed, 265 insertions(+), 93 deletions(-) create mode 100644 lib/train/platforms/detect/os_windows.rb diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 7e9047fd..6b8189a1 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -55,8 +55,8 @@ def self.family(name, condition = {}) # # @return [Hash] with top level family and platforms def self.top_platforms - top_platforms = families.select { |_key, value| value.families.empty? } - top_platforms.merge!(list.select { |_key, value| value.families.empty? }) + top_platforms = list.select { |_key, value| value.families.empty? } + top_platforms.merge!(families.select { |_key, value| value.families.empty? }) top_platforms end diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index fe3dd34e..c2b6df29 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -46,9 +46,13 @@ def self.scan_children(parent) unless plat.detect.nil? puts "---> Testing: #{plat.name} - #{plat.class}" result = instance_eval(&plat.detect) - return get_platform(plat) if result == true && plat.class == Train::Platform - child_result = scan_children(plat) if plat.respond_to?(:children) && !plat.children.nil? - return child_result unless child_result.nil? + if result == true && plat.class == Train::Platform + return get_platform(plat) + elsif result == true && plat.class == Train::Platforms::Family + @platform[:family] = plat.name + child_result = scan_children(plat) if plat.respond_to?(:children) && !plat.children.nil? + return child_result unless child_result.nil? + end end end nil diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index 70a4adbd..8cb44a9b 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -1,57 +1,29 @@ # encoding: utf-8 require 'train/platforms/detect/os_linux' +require 'train/platforms/detect/os_windows' require 'train/platforms/detect/linux_lsb' module Train::Platforms::Detect module OSCommon include Train::Platforms::Detect::Linux include Train::Platforms::Detect::LinuxLSB + include Train::Platforms::Detect::Windows - def detect_family - case uname_s - when /unrecognized command verb/ - @platform[:family] = 'openvms' - when /linux/i - @platform[:family] = 'linux' - when /./ - @platform[:family] = 'unix' - else - # Don't know what this is - @platform[:family] = nil - end - - detect_via_uname if @platform[:family] == 'unix' - - + def winrm? + Object.const_defined?('Train::Transports::WinRM::Connection') && + @backend.class == Train::Transports::WinRM::Connection end - # def detect_family_type # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - - # this will now happen on the detect blocks - # - # return detect_windows if pf == 'windows' - # return detect_darwin if pf == 'darwin' - # return detect_esx if pf == 'esx' - # return detect_openvms if pf =='openvms' - # - # if %w{freebsd netbsd openbsd aix solaris2 hpux}.include?(pf) - # return detect_via_uname - # end - # - # # unix based systems combine the above - # return true if pf == 'unix' and detect_darwin - # return true if pf == 'unix' and detect_esx - # # This is assuming that pf is set to unix, this should be if pf == 'linux' - # return true if pf == 'unix' and detect_arista_eos - # return true if pf == 'unix' and detect_via_uname - - # end - def get_config(path) + # keep a log of files incase multiple checks call the same one + @config_files = {} if @config_files.nil? + return @config_files[path] unless @config_files[path].nil? + res = @backend.run_command("test -f #{path} && cat #{path}") # ignore files that can't be read return nil if res.exit_status != 0 + @config_files[path] = res.stdout res.stdout end diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb index e5872eaf..4ffc5dbc 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/os_linux.rb @@ -18,18 +18,6 @@ def redhatish_version(conf) conf[/release ([\d\.]+)/, 1] end - def detect_linux_arch - @platform[:arch] = uname_m - end - - def detect_linux - # TODO: print an error in this step of the detection - return false if uname_s.nil? || uname_s.empty? - return false if uname_r.nil? || uname_r.empty? - - detect_linux_arch - end - def fetch_os_release data = get_config('/etc/os-release') return if data.nil? diff --git a/lib/train/platforms/detect/os_windows.rb b/lib/train/platforms/detect/os_windows.rb new file mode 100644 index 00000000..095c977a --- /dev/null +++ b/lib/train/platforms/detect/os_windows.rb @@ -0,0 +1,80 @@ +# encoding: utf-8 + +module Train::Platforms::Detect + module Windows + def detect_windows + res = @backend.run_command('cmd /c ver') + return false if res.exit_status != 0 or res.stdout.empty? + + # if the ver contains `Windows`, we know its a Windows system + version = res.stdout.strip + return false unless version.downcase =~ /windows/ + @platform[:family] = 'windows' + + # try to extract release from eg. `Microsoft Windows [Version 6.3.9600]` + release = /\[(?.*)\]/.match(version) + unless release[:name].nil? + # release is 6.3.9600 now + @platform[:release] = release[:name].downcase.gsub('version', '').strip + # fallback, if we are not able to extract the name from wmic later + @platform[:name] = "Windows #{@platform[:release]}" + end + + # try to use wmic, but lets keep it optional + read_wmic + + @platform[:type] = 'windows' + true + end + + # reads os name and version from wmic + # @see https://msdn.microsoft.com/en-us/library/bb742610.aspx#EEAA + # Thanks to Matt Wrock (https://github.com/mwrock) for this hint + def read_wmic + res = @backend.run_command('wmic os get * /format:list') + if res.exit_status == 0 + sys_info = {} + res.stdout.lines.each { |line| + m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line) + sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil? + } + + @platform[:release] = sys_info[:Version] + # additional info on windows + @platform[:build] = sys_info[:BuildNumber] + @platform[:name] = sys_info[:Caption] + @platform[:name] = @platform[:name].gsub('Microsoft', '').strip unless @platform[:name].empty? + @platform[:arch] = read_wmic_cpu + end + end + + # `OSArchitecture` from `read_wmic` does not match a normal standard + # For example, `x86_64` shows as `64-bit` + def read_wmic_cpu + res = @backend.run_command('wmic cpu get architecture /format:list') + if res.exit_status == 0 + sys_info = {} + res.stdout.lines.each { |line| + m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line) + sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil? + } + end + + # This converts `wmic os get architecture` output to a normal standard + # https://msdn.microsoft.com/en-us/library/aa394373(VS.85).aspx + arch_map = { + 0 => 'i386', + 1 => 'mips', + 2 => 'alpha', + 3 => 'powerpc', + 5 => 'arm', + 6 => 'ia64', + 9 => 'x86_64', + } + + # The value of `wmic cpu get architecture` is always a number between 0-9 + arch_number = sys_info[:Architecture].to_i + arch_map[arch_number] + end + end +end diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/specifications/os.rb index 101f6f74..f464bf97 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -3,7 +3,9 @@ # windows platform plat.name('windows') .detect { - return true if @platform[:name] =~ /windows|microsoft/i + if winrm? + return true if detect_windows == true + end } # unix master family @@ -23,7 +25,7 @@ plat.family('linux').in_family('unix') .detect { if @platform[:family] == 'linux' - detect_linux_arch # sets arch in @platform[:arch] + @platform[:arch] = uname_m true end } @@ -64,7 +66,6 @@ true end } -plat.name('backtrack', release: '>= 4').in_family('debian') # redhat family plat.family('redhat').in_family('linux') @@ -75,21 +76,27 @@ true } -plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') +plat.name('centos').title('Centos Linux').in_family('redhat') .detect { lsb = detect_linux_lsb - if lsb && lsb[:id] =~ /redhat/i + if lsb && lsb[:id] =~ /centos/i @platform[:release] = lsb[:release] true + elsif get_config('/etc/os-release') =~ /centos/i + @platform[:release] = redhatish_version(get_config('/etc/redhat-release')) + true end } -plat.name('centos').title('Centos Linux').in_family('redhat') +# keep redhat after centos as a catchall for redhat base +plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') .detect { lsb = detect_linux_lsb - if lsb && lsb[:id] =~ /centos/i + if lsb && lsb[:id] =~ /redhat/i @platform[:release] = lsb[:release] true - elsif get_config('/etc/os-release') =~ /centos/i + elsif !(raw = get_config('/etc/redhat-release')).nil? + # must be some type of redhat + @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) true end @@ -97,36 +104,28 @@ plat.name('oracle').title('Oracle Linux').in_family('redhat') .detect { if !(raw = get_config('/etc/oracle-release')).nil? - @platform[:name] = 'oracle' @platform[:release] = redhatish_version(raw) true elsif !(raw = get_config('/etc/enterprise-release')).nil? - @platform[:name] = 'oracle' @platform[:release] = redhatish_version(raw) true end } -plat.name('amazon').title('Amazon Linux').in_family('redhat') +plat.name('scientific').title('Scientific Linux').in_family('redhat') .detect { lsb = detect_linux_lsb - if lsb && lsb[:id] =~ /amazon/i - @platform[:name] = 'amazon' + if lsb && lsb[:id] =~ /scientificsl/i @platform[:release] = lsb[:release] true - elsif !(raw = get_config('/etc/system-release')).nil? - # Amazon Linux - @platform[:name] = redhatish_platform(raw) - @platform[:release] = redhatish_version(raw) - true end } -plat.name('scientific').title('Scientific Linux').in_family('redhat') - .detect { - # for now this is detected at the start during the lsb call - } plat.name('xenserver').title('Xenserer Linux').in_family('redhat') .detect { - # for now this is detected at the start during the lsb call + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /xenserver/i + @platform[:release] = lsb[:release] + true + end } plat.name('parallels-release').title('Parallels Linux').in_family('redhat') .detect { @@ -138,19 +137,32 @@ } plat.name('wrlinux').title('Wind River Linux').in_family('redhat') .detect { - if !(os_info = fetch_os_release).nil? - if os_info['ID_LIKE'] =~ /wrlinux/ + if !(os_info = get_config('/etc/os-release')).nil? + if os_info =~ /wrlinux/ @platform[:name] = 'wrlinux' - @platform[:release] = os_info['VERSION'] + @platform[:release] = os_info(/VERSION(.*)/) true end end } +plat.name('amazon').title('Amazon Linux').in_family('redhat') + .detect { + lsb = detect_linux_lsb + if lsb && lsb[:id] =~ /amazon/i + @platform[:release] = lsb[:release] + true + elsif !(raw = get_config('/etc/system-release')).nil? + @platform[:name] = redhatish_platform(raw) + @platform[:release] = redhatish_version(raw) + true + end + } # suse family plat.family('suse').in_family('linux') .detect { if !(suse = get_config('/etc/SuSE-release')).nil? + puts suse.inspect version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' @platform[:release] = version @@ -160,16 +172,12 @@ plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') .detect { return true if get_config('/etc/SuSE-release') =~ /^openSUSE/ - # release set at the family level } plat.name('suse').title('Suse Linux').in_family('suse') .detect { return true if get_config('/etc/SuSE-release') =~ /suse/ - # release set at the family level } - - # arch plat.name('arch').title('Arch Linux').in_family('linux') .detect { @@ -229,11 +237,69 @@ } # genaric linux +# this should always be last in the linux family list plat.name('linux').title('Genaric Linux').in_family('linux') .detect { true } +# openvms +plat.name('openvms').title('OpenVMS').in_family('unix') + .detect { + if uname_s =~ /unrecognized command verb/i + cmd = @backend.run_command('show system/noprocess') + unless cmd.exit_status != 0 || cmd.stdout.empty? + @platform[:name] = cmd.stdout.downcase.split(' ')[0] + cmd = @backend.run_command('write sys$output f$getsyi("VERSION")') + @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1] + cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")') + @platform[:arch] = cmd.stdout.downcase.split("\n")[1] + true + end + end + } + +# arista_eos family +plat.family('arista_eos').in_family('unix') + .detect { + # we need a better way to determin this family + true + } +plat.name('arista_eos').title('Arista EOS').in_family('arista_eos') + .detect { + cmd = @backend.run_command('show version | json') + if cmd.exit_status == 0 && !cmd.stdout.empty? + require 'json' + eos_ver = JSON.parse(cmd.stdout) + @platform[:release] = eos_ver['version'] + @platform[:arch] = eos_ver['architecture'] + true + end + } +plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') + .detect { + if unix_file?('/usr/bin/FastCli') + cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') + if cmd.exit_status == 0 && !cmd.stdout.empty? + require 'json' + eos_ver = JSON.parse(cmd.stdout) + @platform[:release] = eos_ver['version'] + @platform[:arch] = eos_ver['architecture'] + true + end + end + } + +# esx +plat.name('esx').title('ESX').in_family('unix') + .detect { + if uname_s =~ /vmkernel/i + @platform[:name] = uname_s.lines[0].chomp + @platform[:release] = uname_r.lines[0].chomp + true + end + } + # aix plat.name('aix').title('Aix').in_family('unix') .detect { @@ -251,15 +317,67 @@ # solaris family plat.family('solaris').in_family('unix') .detect { - # we need a better way to determin this family - true - } + if uname_s =~ /sunos/i + unless (version = /^5\.(?\d+)$/.match(uname_r)).nil? + @platform[:release] = version['release'] + end -# solaris + arch = @backend.run_command('uname -p') + @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0 + true + end + } +plat.name('smartos').title('SmartOS').in_family('solaris') + .detect { + rel = get_config('/etc/release') + if /^.*(SmartOS).*$/ =~ rel + true + end + } +plat.name('omnios').title('Omnios').in_family('solaris') + .detect { + rel = get_config('/etc/release') + if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil? + @platform[:release] = m[2] + true + end + } +plat.name('openindiana').title('Openindiana').in_family('solaris') + .detect { + rel = get_config('/etc/release') + if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil? + @platform[:release] = m[2] + true + end + } +plat.name('opensolaris').title('Open Solaris').in_family('solaris') + .detect { + rel = get_config('/etc/release') + if /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel + @platform[:release] = m[2] + true + end + } +plat.name('nexentacore').title('Nexentacore').in_family('solaris') + .detect { + rel = get_config('/etc/release') + if /^\s*(NexentaCore)\s.*$/ =~ rel + true + end + } plat.name('solaris').title('Solaris').in_family('solaris') .detect { - # TODO: REDO - true if @platform[:name] =~ /solaris/ + rel = get_config('/etc/release') + if !(m = /Oracle Solaris (\d+)/.match(rel)).nil? + # TODO: should be string! + @platform[:release] = m[1] + true + elsif /^\s*(Solaris)\s.*$/ =~ rel + true + else + # must be some unknown solaris + true + end } # hpux @@ -274,7 +392,6 @@ # bsd family plat.family('bsd').in_family('unix') .detect { - # we need a better way to determin this family true } plat.name('darwin').title('Darwin').in_family('bsd') @@ -284,6 +401,13 @@ @platform[:release] = uname_r.lines[0].chomp true end + cmd = @backend.run_command('/usr/bin/sw_vers') + return nil if cmd.exit_status != 0 || cmd.stdout.empty? + + @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1] + @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1] + @platform[:arch] = uname_m + true } plat.name('freebsd').title('Freebsd').in_family('bsd') .detect { diff --git a/lib/train/transports/docker.rb b/lib/train/transports/docker.rb index 15e1b7c6..a5be1004 100644 --- a/lib/train/transports/docker.rb +++ b/lib/train/transports/docker.rb @@ -70,7 +70,11 @@ def close end def os - @os ||= OS.new(self) + platform + end + + def platform + @platform ||= Train::Platforms::Detect.scan(self) end def file(path) From ab3a04c1dcb97de1be711949d826e52ddc3c8a9d Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Tue, 24 Oct 2017 14:48:04 -0400 Subject: [PATCH 08/22] Add dynamic family and platform methods and refactor os specification Signed-off-by: Jared Quick --- lib/train/errors.rb | 3 + lib/train/extras.rb | 1 - lib/train/extras/linux_lsb.rb | 60 --------- lib/train/extras/os_common.rb | 151 --------------------- lib/train/extras/os_detect_arista_eos.rb | 34 ----- lib/train/extras/os_detect_darwin.rb | 40 ------ lib/train/extras/os_detect_esx.rb | 22 --- lib/train/extras/os_detect_linux.rb | 164 ----------------------- lib/train/extras/os_detect_openvms.rb | 29 ---- lib/train/extras/os_detect_unix.rb | 100 -------------- lib/train/extras/os_detect_windows.rb | 86 ------------ lib/train/extras/uname.rb | 28 ---- lib/train/platform.rb | 15 ++- lib/train/platforms.rb | 24 +++- lib/train/platforms/detect.rb | 83 +++++++----- lib/train/platforms/detect/linux_lsb.rb | 6 +- lib/train/platforms/detect/os_common.rb | 10 +- lib/train/platforms/detect/os_linux.rb | 23 ---- lib/train/platforms/detect/os_local.rb | 38 ------ lib/train/platforms/specifications/os.rb | 102 +++++++------- lib/train/transports/docker.rb | 8 -- lib/train/transports/mock.rb | 34 +++-- lib/train/transports/winrm_connection.rb | 12 +- 23 files changed, 164 insertions(+), 909 deletions(-) delete mode 100644 lib/train/extras/linux_lsb.rb delete mode 100644 lib/train/extras/os_common.rb delete mode 100644 lib/train/extras/os_detect_arista_eos.rb delete mode 100644 lib/train/extras/os_detect_darwin.rb delete mode 100644 lib/train/extras/os_detect_esx.rb delete mode 100644 lib/train/extras/os_detect_linux.rb delete mode 100644 lib/train/extras/os_detect_openvms.rb delete mode 100644 lib/train/extras/os_detect_unix.rb delete mode 100644 lib/train/extras/os_detect_windows.rb delete mode 100644 lib/train/extras/uname.rb delete mode 100644 lib/train/platforms/detect/os_local.rb diff --git a/lib/train/errors.rb b/lib/train/errors.rb index 6a506952..b3fb8411 100644 --- a/lib/train/errors.rb +++ b/lib/train/errors.rb @@ -20,4 +20,7 @@ class ClientError < ::StandardError; end # Base exception class for all exceptions that are caused by other failures # in the transport layer. class TransportError < ::StandardError; end + + # Exception for when no platform can be detected + class PlatformDetectionFailed < ::StandardError; end end diff --git a/lib/train/extras.rb b/lib/train/extras.rb index 74385004..697dd887 100644 --- a/lib/train/extras.rb +++ b/lib/train/extras.rb @@ -9,7 +9,6 @@ module Train::Extras require 'train/extras/file_aix' require 'train/extras/file_linux' require 'train/extras/file_windows' - require 'train/extras/os_common' require 'train/extras/stat' CommandResult = Struct.new(:stdout, :stderr, :exit_status) diff --git a/lib/train/extras/linux_lsb.rb b/lib/train/extras/linux_lsb.rb deleted file mode 100644 index 46e73d53..00000000 --- a/lib/train/extras/linux_lsb.rb +++ /dev/null @@ -1,60 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# - -module Train::Extras - module LinuxLSB - def lsb_config(content) - { - id: content[/^DISTRIB_ID=["']?(.+?)["']?$/, 1], - release: content[/^DISTRIB_RELEASE=["']?(.+?)["']?$/, 1], - codename: content[/^DISTRIB_CODENAME=["']?(.+?)["']?$/, 1], - } - end - - def lsb_release - raw = @backend.run_command('lsb_release -a').stdout - { - id: raw[/^Distributor ID:\s+(.+)$/, 1], - release: raw[/^Release:\s+(.+)$/, 1], - codename: raw[/^Codename:\s+(.+)$/, 1], - } - end - - def lsb - return @lsb if defined?(@lsb) - @lsb = {} - if !(raw = get_config('/etc/lsb-release')).nil? - @lsb = lsb_config(raw) - elsif unix_file?('/usr/bin/lsb_release') - @lsb = lsb_release - end - @lsb - end - - def detect_linux_via_lsb - return false if lsb[:id].nil? - id = lsb[:id].downcase - case id - when /redhat/ - @platform[:family] = 'redhat' - when /amazon/ - @platform[:family] = 'amazon' - when /scientificsl/ - @platform[:family] = 'scientific' - when /xenserver/ - @platform[:family] = 'xenserver' - else - @platform[:family] = id - end - @platform[:release] = lsb[:release] - true - end - end -end diff --git a/lib/train/extras/os_common.rb b/lib/train/extras/os_common.rb deleted file mode 100644 index 7668e56f..00000000 --- a/lib/train/extras/os_common.rb +++ /dev/null @@ -1,151 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# - -require 'train/extras/os_detect_darwin' -require 'train/extras/os_detect_linux' -require 'train/extras/os_detect_unix' -require 'train/extras/os_detect_windows' -require 'train/extras/os_detect_esx' -require 'train/extras/os_detect_arista_eos' -require 'train/extras/os_detect_openvms' - -module Train::Extras - class OSCommon - include Train::Extras::DetectDarwin - include Train::Extras::DetectLinux - include Train::Extras::DetectUnix - include Train::Extras::DetectWindows - include Train::Extras::DetectEsx - include Train::Extras::DetectAristaEos - include Train::Extras::DetectOpenVMS - - attr_accessor :backend - def initialize(backend, platform = nil) - @backend = backend - @platform = platform || {} - detect_family - end - - def [](key) - @platform[key] - end - - def to_hash - @platform - end - - OS = { # rubocop:disable Style/MutableConstant - 'redhat' => REDHAT_FAMILY, - 'debian' => DEBIAN_FAMILY, - 'suse' => SUSE_FAMILY, - 'fedora' => %w{fedora}, - 'bsd' => %w{ - freebsd netbsd openbsd darwin - }, - 'solaris' => %w{ - solaris smartos omnios openindiana opensolaris nexentacore - }, - 'windows' => %w{ - windows - }, - 'aix' => %w{ - aix - }, - 'hpux' => %w{ - hpux - }, - 'esx' => %w{ - esx - }, - 'darwin' => %w{ - darwin - }, - } - - OS['linux'] = %w{linux alpine arch coreos exherbo gentoo slackware fedora amazon} + OS['redhat'] + OS['debian'] + OS['suse'] - - OS['unix'] = %w{unix aix hpux} + OS['linux'] + OS['solaris'] + OS['bsd'] - - # Helper methods to check the OS type - # Provides methods in the form of: linux?, unix?, solaris? ... - OS.keys.each do |os_family| - define_method((os_family + '?').to_sym) do - OS[os_family].include?(@platform[:family]) - end - end - - private - - def detect_family - # if some information is already defined, try to verify it - # with the remaining detection - unless @platform[:family].nil? - # return ok if the preconfigured family yielded a good result - return true if detect_family_type - # if not, reset the platform to presets and run the full detection - # TODO: print an error message in this case, as the instantiating - # backend is doing something wrong - @platform = {} - end - - # TODO: extend base implementation for detecting the family type - # to Windows and others - case uname_s - when /unrecognized command verb/ - @platform[:family] = 'openvms' - when /linux/i - @platform[:family] = 'linux' - when /./ - @platform[:family] = 'unix' - else - # Don't know what this is - @platform[:family] = nil - end - - # try to detect the platform if the platform is set to nil, otherwise this code will never work - return nil if @platform[:family].nil? - detect_family_type - end - - def detect_family_type # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - pf = @platform[:family] - - return detect_windows if pf == 'windows' - return detect_darwin if pf == 'darwin' - return detect_esx if pf == 'esx' - return detect_openvms if pf =='openvms' - - if %w{freebsd netbsd openbsd aix solaris2 hpux}.include?(pf) - return detect_via_uname - end - - # unix based systems combine the above - return true if pf == 'unix' and detect_darwin - return true if pf == 'unix' and detect_esx - # This is assuming that pf is set to unix, this should be if pf == 'linux' - return true if pf == 'unix' and detect_arista_eos - return true if pf == 'unix' and detect_via_uname - - # if we arrive here, we most likey have a regular linux - detect_linux - end - - def get_config(path) - res = @backend.run_command("test -f #{path} && cat #{path}") - # ignore files that can't be read - return nil if res.exit_status != 0 - res.stdout - end - - def unix_file?(path) - @backend.run_command("test -f #{path}").exit_status == 0 - end - end -end diff --git a/lib/train/extras/os_detect_arista_eos.rb b/lib/train/extras/os_detect_arista_eos.rb deleted file mode 100644 index 597eb3a5..00000000 --- a/lib/train/extras/os_detect_arista_eos.rb +++ /dev/null @@ -1,34 +0,0 @@ -# encoding: utf-8 -# author: Jere Julian -# -# Arista EOS has 2 modes. Most compliance tests will use the network CLI -# but when working with vagrant, its common to encounter the raw bash shell. -require 'json' - -module Train::Extras - module DetectAristaEos - def detect_arista_eos - if unix_file?('/usr/bin/FastCli') - cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') - @platform[:name] = 'arista_eos_bash' - family = 'fedora' - else - cmd = @backend.run_command('show version | json') - end - - # in PTY mode, stderr is matched with stdout, therefore it may not be empty - output = cmd.stdout - if cmd.exit_status == 0 && !output.empty? - eos_ver = JSON.parse(output) - @platform[:name] = @platform[:name] || 'arista_eos' - family ||= 'arista_eos' - @platform[:family] = family - @platform[:release] = eos_ver['version'] - @platform[:arch] = eos_ver['architecture'] - true - else - false - end - end - end -end diff --git a/lib/train/extras/os_detect_darwin.rb b/lib/train/extras/os_detect_darwin.rb deleted file mode 100644 index cc3287d1..00000000 --- a/lib/train/extras/os_detect_darwin.rb +++ /dev/null @@ -1,40 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# -require 'train/extras/uname' - -module Train::Extras - module DetectDarwin - include Train::Extras::Uname - - def detect_darwin - cmd = @backend.run_command('/usr/bin/sw_vers') - # TODO: print an error in this step of the detection, - # as it shouldnt happen - return false if cmd.exit_status != 0 - # TODO: ditto on error - return false if cmd.stdout.empty? - - name = cmd.stdout[/^ProductName:\s+(.+)$/, 1] - # TODO: ditto on error - return false if name.nil? - @platform[:name] = name.downcase.chomp.tr(' ', '_') - @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1] - @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1] - # TODO: keep for now due to backwards compatibility with serverspec - @platform[:family] = 'darwin' - detect_darwin_arch - true - end - - def detect_darwin_arch - @platform[:arch] = uname_m - end - end -end diff --git a/lib/train/extras/os_detect_esx.rb b/lib/train/extras/os_detect_esx.rb deleted file mode 100644 index b05ab1f8..00000000 --- a/lib/train/extras/os_detect_esx.rb +++ /dev/null @@ -1,22 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# - -module Train::Extras - module DetectEsx - def detect_esx - if uname_s.downcase.chomp == 'vmkernel' - @platform[:family] = 'esx' - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - true - end - end - end -end diff --git a/lib/train/extras/os_detect_linux.rb b/lib/train/extras/os_detect_linux.rb deleted file mode 100644 index e9f36585..00000000 --- a/lib/train/extras/os_detect_linux.rb +++ /dev/null @@ -1,164 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# - -require 'train/extras/linux_lsb' -require 'train/extras/uname' - -module Train::Extras - module DetectLinux # rubocop:disable Metrics/ModuleLength - DEBIAN_FAMILY = %w{debian ubuntu linuxmint raspbian}.freeze - REDHAT_FAMILY = %w{centos redhat oracle scientific enterpriseenterprise xenserver cloudlinux ibm_powerkvm nexus_centos wrlinux virtuozzo parallels}.freeze - SUSE_FAMILY = %w{suse opensuse}.freeze - - include Train::Extras::LinuxLSB - include Train::Extras::Uname - - def detect_linux_via_config # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity - if !(raw = get_config('/etc/oracle-release')).nil? - @platform[:name] = 'oracle' - @platform[:release] = redhatish_version(raw) - elsif !(raw = get_config('/etc/enterprise-release')).nil? - @platform[:name] = 'oracle' - @platform[:release] = redhatish_version(raw) - elsif !(raw = get_config('/etc/debian_version')).nil? - case lsb[:id] - when /ubuntu/i - @platform[:name] = 'ubuntu' - @platform[:release] = lsb[:release] - when /linuxmint/i - @platform[:name] = 'linuxmint' - @platform[:release] = lsb[:release] - else - @platform[:name] = unix_file?('/usr/bin/raspi-config') ? 'raspbian' : 'debian' - @platform[:release] = raw.chomp - end - elsif !(raw = get_config('/etc/parallels-release')).nil? - @platform[:name] = redhatish_platform(raw) - @platform[:release] = raw[/(\d\.\d\.\d)/, 1] - elsif !(raw = get_config('/etc/redhat-release')).nil? - # TODO: Cisco - # TODO: fully investigate os-release and integrate it; - # here we just use it for centos - @platform[:name] = if !(osrel = get_config('/etc/os-release')).nil? && osrel =~ /centos/i - 'centos' - else - redhatish_platform(raw) - end - - @platform[:release] = redhatish_version(raw) - elsif !(raw = get_config('/etc/system-release')).nil? - # Amazon Linux - @platform[:name] = redhatish_platform(raw) - @platform[:release] = redhatish_version(raw) - elsif !(suse = get_config('/etc/SuSE-release')).nil? - version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') - version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' - @platform[:release] = version - @platform[:name] = if suse =~ /^openSUSE/ - 'opensuse' - else - 'suse' - end - elsif !(raw = get_config('/etc/arch-release')).nil? - @platform[:name] = 'arch' - # Because this is a rolling release distribution, - # use the kernel release, ex. 4.1.6-1-ARCH - @platform[:release] = uname_r - elsif !(raw = get_config('/etc/slackware-version')).nil? - @platform[:name] = 'slackware' - @platform[:release] = raw.scan(/(\d+|\.+)/).join - elsif !(raw = get_config('/etc/exherbo-release')).nil? - @platform[:name] = 'exherbo' - # Because this is a rolling release distribution, - # use the kernel release, ex. 4.1.6 - @platform[:release] = uname_r - elsif !(raw = get_config('/etc/gentoo-release')).nil? - @platform[:name] = 'gentoo' - @platform[:release] = raw.scan(/(\d+|\.+)/).join - elsif !(raw = get_config('/etc/alpine-release')).nil? - @platform[:name] = 'alpine' - @platform[:release] = raw.strip - elsif !get_config('/etc/coreos/update.conf').nil? - @platform[:name] = 'coreos' - @platform[:release] = lsb[:release] - elsif !(os_info = fetch_os_release).nil? - if os_info['ID_LIKE'] =~ /wrlinux/ - @platform[:name] = 'wrlinux' - @platform[:release] = os_info['VERSION'] - end - end - - @platform[:family] = family_for_platform - - !@platform[:family].nil? && !@platform[:release].nil? - end - - def family_for_platform - if DEBIAN_FAMILY.include?(@platform[:name]) - 'debian' - elsif REDHAT_FAMILY.include?(@platform[:name]) - 'redhat' - elsif SUSE_FAMILY.include?(@platform[:name]) - 'suse' - else - @platform[:name] || @platform[:family] - end - end - - def redhatish_platform(conf) - conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase - end - - def redhatish_version(conf) - return conf[/((\d+) \(Rawhide\))/i, 1].downcase if conf[/rawhide/i] - return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i] - conf[/release ([\d\.]+)/, 1] - end - - def detect_linux_arch - @platform[:arch] = uname_m - end - - def detect_linux - # TODO: print an error in this step of the detection - return false if uname_s.nil? || uname_s.empty? - return false if uname_r.nil? || uname_r.empty? - - detect_linux_arch - return true if detect_linux_via_config - return true if detect_linux_via_lsb - # in all other cases we failed the detection - @platform[:family] = 'unknown' - end - - def fetch_os_release - data = get_config('/etc/os-release') - return if data.nil? - - os_info = parse_os_release_info(data) - cisco_info_file = os_info['CISCO_RELEASE_INFO'] - if cisco_info_file - os_info.merge!(parse_os_release_info(get_config(cisco_info_file))) - end - - os_info - end - - def parse_os_release_info(raw) - return {} if raw.nil? - - raw.lines.each_with_object({}) do |line, memo| - line.strip! - key, value = line.split('=', 2) - memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty? - end - end - end -end diff --git a/lib/train/extras/os_detect_openvms.rb b/lib/train/extras/os_detect_openvms.rb deleted file mode 100644 index 18f863d3..00000000 --- a/lib/train/extras/os_detect_openvms.rb +++ /dev/null @@ -1,29 +0,0 @@ -# encoding: utf-8 -# author: Brian Doody (HPE) -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# -require 'train/extras/uname' - -module Train::Extras - module DetectOpenVMS - include Train::Extras::Uname - - def detect_openvms - cmd = @backend.run_command('show system/noprocess') - - return false if cmd.exit_status != 0 - return false if cmd.stdout.empty? - - @platform[:name] = cmd.stdout.downcase.split(' ')[0] - cmd = @backend.run_command('write sys$output f$getsyi("VERSION")') - @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1] - cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")') - @platform[:arch] = cmd.stdout.downcase.split("\n")[1] - - true - end - end -end diff --git a/lib/train/extras/os_detect_unix.rb b/lib/train/extras/os_detect_unix.rb deleted file mode 100644 index 09c24af6..00000000 --- a/lib/train/extras/os_detect_unix.rb +++ /dev/null @@ -1,100 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# - -module Train::Extras - module DetectUnix - def detect_via_uname # rubocop:disable Metrics/AbcSize - case uname_s.downcase - when /aix/ - @platform[:family] = 'aix' - @platform[:name] = uname_s.lines[0].chomp - out = @backend.run_command('uname -rvp').stdout - m = out.match(/(\d+)\s+(\d+)\s+(.*)/) - unless m.nil? - @platform[:release] = "#{m[2]}.#{m[1]}" - @platform[:arch] = m[3].to_s - end - when /hp-ux/ - @platform[:family] = 'hpux' - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - - when /freebsd/ - @platform[:family] = 'freebsd' - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - - when /netbsd/ - @platform[:family] = 'netbsd' - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - - when /openbsd/ - @platform[:family] = 'openbsd' - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - - when /sunos/ - detect_solaris - else - # in all other cases we didn't detect it - return false - end - # when we get here the detection returned a result - true - end - - def detect_solaris # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity - # read specific os name - # DEPRECATED: os[:family] is going to be deprecated, use os.solaris? - rel = get_config('/etc/release') - if /^.*(SmartOS).*$/ =~ rel - @platform[:name] = 'smartos' - @platform[:family] = 'smartos' - elsif !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil? - @platform[:name] = 'omnios' - @platform[:family] = 'omnios' - @platform[:release] = m[2] - elsif !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil? - @platform[:name] = 'openindiana' - @platform[:family] = 'openindiana' - @platform[:release] = m[2] - elsif /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel - @platform[:name] = 'opensolaris' - @platform[:family] = 'opensolaris' - @platform[:release] = m[2] - elsif !(m = /Oracle Solaris (\d+)/.match(rel)).nil? - # TODO: should be string! - @platform[:release] = m[1] - @platform[:name] = 'solaris' - @platform[:family] = 'solaris' - elsif /^\s*(Solaris)\s.*$/ =~ rel - @platform[:name] = 'solaris' - @platform[:family] = 'solaris' - elsif /^\s*(NexentaCore)\s.*$/ =~ rel - @platform[:name] = 'nexentacore' - @platform[:family] = 'nexentacore' - else - # unknown solaris - @platform[:name] = 'solaris_distro' - @platform[:family] = 'solaris' - end - - # read release version - unless (version = /^5\.(?\d+)$/.match(uname_r)).nil? - @platform[:release] = version['release'] - end - - # read architecture - arch = @backend.run_command('uname -p') - @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0 - end - end -end diff --git a/lib/train/extras/os_detect_windows.rb b/lib/train/extras/os_detect_windows.rb deleted file mode 100644 index 06d47a67..00000000 --- a/lib/train/extras/os_detect_windows.rb +++ /dev/null @@ -1,86 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# -module Train::Extras - module DetectWindows - def detect_windows - res = @backend.run_command('cmd /c ver') - return false if res.exit_status != 0 or res.stdout.empty? - - # if the ver contains `Windows`, we know its a Windows system - version = res.stdout.strip - return false unless version.downcase =~ /windows/ - @platform[:family] = 'windows' - - # try to extract release from eg. `Microsoft Windows [Version 6.3.9600]` - release = /\[(?.*)\]/.match(version) - unless release[:name].nil? - # release is 6.3.9600 now - @platform[:release] = release[:name].downcase.gsub('version', '').strip - # fallback, if we are not able to extract the name from wmic later - @platform[:name] = "Windows #{@platform[:release]}" - end - - # try to use wmic, but lets keep it optional - read_wmic - - true - end - - # reads os name and version from wmic - # @see https://msdn.microsoft.com/en-us/library/bb742610.aspx#EEAA - # Thanks to Matt Wrock (https://github.com/mwrock) for this hint - def read_wmic - res = @backend.run_command('wmic os get * /format:list') - if res.exit_status == 0 - sys_info = {} - res.stdout.lines.each { |line| - m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line) - sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil? - } - - @platform[:release] = sys_info[:Version] - # additional info on windows - @platform[:build] = sys_info[:BuildNumber] - @platform[:name] = sys_info[:Caption] - @platform[:name] = @platform[:name].gsub('Microsoft', '').strip unless @platform[:name].empty? - @platform[:arch] = read_wmic_cpu - end - end - - # `OSArchitecture` from `read_wmic` does not match a normal standard - # For example, `x86_64` shows as `64-bit` - def read_wmic_cpu - res = @backend.run_command('wmic cpu get architecture /format:list') - if res.exit_status == 0 - sys_info = {} - res.stdout.lines.each { |line| - m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line) - sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil? - } - end - - # This converts `wmic os get architecture` output to a normal standard - # https://msdn.microsoft.com/en-us/library/aa394373(VS.85).aspx - arch_map = { - 0 => 'i386', - 1 => 'mips', - 2 => 'alpha', - 3 => 'powerpc', - 5 => 'arm', - 6 => 'ia64', - 9 => 'x86_64', - } - - # The value of `wmic cpu get architecture` is always a number between 0-9 - arch_number = sys_info[:Architecture].to_i - arch_map[arch_number] - end - end -end diff --git a/lib/train/extras/uname.rb b/lib/train/extras/uname.rb deleted file mode 100644 index 4b45abe5..00000000 --- a/lib/train/extras/uname.rb +++ /dev/null @@ -1,28 +0,0 @@ -# encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann -# -# This is heavily based on: -# -# OHAI https://github.com/chef/ohai -# by Adam Jacob, Chef Software Inc -# -module Train::Extras - module Uname - def uname_s - @uname_s ||= backend.run_command('uname -s').stdout - end - - def uname_r - @uname_r ||= begin - res = backend.run_command('uname -r').stdout - res.strip! unless res.nil? - res - end - end - - def uname_m - @uname_m ||= backend.run_command('uname -m').stdout.chomp - end - end -end diff --git a/lib/train/platform.rb b/lib/train/platform.rb index 08d837c5..3482c43a 100644 --- a/lib/train/platform.rb +++ b/lib/train/platform.rb @@ -3,21 +3,26 @@ module Train class Platform include Train::Platforms::Common - attr_accessor :name, :condition, :families, :backend, :platform + attr_accessor :name, :condition, :families, :backend, :platform, :family_hierarchy def initialize(name, condition = {}) @condition = condition @name = name @families = {} + @family_hierarchy = [] # add itself to the platform list Train::Platforms.list[name] = self end - %w{unix? windows?}.each do |m| - define_method m do |_arg = nil| - @platform[:type] == m - end + def direct_families + @families.collect { |k, _v| k.name } + end + + # This is for backwords compatability with + # the current inspec os resource. + def[](name) + send(name) end define_method "#{name}?" do |_arg = nil| diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 6b8189a1..1e3c927e 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -60,13 +60,33 @@ def self.top_platforms top_platforms end + # Add genaric family? and platform methods to an existing platform + # + # This is done later to add any custom + # families/properties that were created + def self.add_platform_methods(plat) + families.each_value do |k| + plat.class.send(:define_method, k.name + '?') { + plat.family_hierarchy.include?(k.name) + } + end + + # Helper methods for direct platform info + # Note: this can override platform.name if a + # custom one was found during detect block + plat.platform.each_key do |m| + plat.class.send(:define_method, m) { + @platform[m] + } + end + end + # List all platforms and families in a readable output def self.list_all top_platforms = self.top_platforms top_platforms.each_value do |platform| puts "#{platform.title} (#{platform.class})" - print_children(platform) if defined?(platform.children) - end + print_children(platform) if defined?(platform.children) end end def self.print_children(parent, pad = 2) diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index c2b6df29..deba7db8 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -2,68 +2,77 @@ require 'train/platforms/specifications/os' require 'train/platforms/detect/os_common' -require 'train/platforms/detect/os_local' module Train::Platforms::Detect extend Train::Platforms::Detect::OSCommon - extend Train::Platforms::Detect::OSLocal + # Main detect method to scan all platforms for a match + # + # @return Train::Platform instance or error if none found def self.scan(backend) @backend = backend @platform = {} + @family_hierarchy = [] - # grab local platform info if we are running local - if @backend.local? - plat = detect_local_os - return get_platform(plat) if plat - end - - # Start at the top + # start with the platform/families who have no families (the top levels) top = Train::Platforms.top_platforms top.each do |name, plat| puts "---> Testing-TOP: #{name} - #{plat.class}" - if plat.detect - result = instance_eval(&plat.detect) - # if we have a match start looking at the children - if result - child_result = scan_children(plat) - return child_result unless child_result.nil? - end - end + next unless plat.detect + result = instance_eval(&plat.detect) + next unless result == true + + # if we have a match start looking at the children + plat_result = scan_children(plat) + next if plat_result.nil? + + # return platform to backend + @family_hierarchy << plat.name + plat_result.family_hierarchy = @family_hierarchy + return plat_result end - # raise Train::Exceptions::PlatformDetectionFailed 'Sorry we did not find your platform' - raise 'Sorry we did not find your platform' + raise Train::PlatformDetectionFailed, 'Sorry we did not find your platform' end def self.scan_children(parent) parent.children.each do |plat, condition| - if condition - condition.each do |k, v| - return nil if @platform[k] != v - end - end - unless plat.detect.nil? - puts "---> Testing: #{plat.name} - #{plat.class}" - result = instance_eval(&plat.detect) - if result == true && plat.class == Train::Platform - return get_platform(plat) - elsif result == true && plat.class == Train::Platforms::Family - @platform[:family] = plat.name - child_result = scan_children(plat) if plat.respond_to?(:children) && !plat.children.nil? - return child_result unless child_result.nil? - end + next if plat.detect.nil? + puts "---> Testing: #{plat.name} - #{plat.class}" + result = instance_eval(&plat.detect) + next unless result == true + + if plat.class == Train::Platform + @platform[:family] = parent.name + return get_platform(plat) if condition.empty? || condition_check(condition) + elsif plat.class == Train::Platforms::Family + plat = scan_family_children(plat) + return plat unless plat.nil? end end + nil end + def self.scan_family_children(plat) + child_result = scan_children(plat) if !plat.children.nil? + return if child_result.nil? + @family_hierarchy << plat.name + child_result + end + + def self.check_condition(condition) + condition.each do |k, v| + return false unless instance_eval("#{@platform[k]} #{v}") + end + + true + end + def self.get_platform(plat) plat.backend = @backend plat.platform = @platform - puts "---" - puts plat.name - puts plat.platform.inspect + Train::Platforms.add_platform_methods(plat) plat end end diff --git a/lib/train/platforms/detect/linux_lsb.rb b/lib/train/platforms/detect/linux_lsb.rb index abc85ca0..80da3e1f 100644 --- a/lib/train/platforms/detect/linux_lsb.rb +++ b/lib/train/platforms/detect/linux_lsb.rb @@ -22,15 +22,15 @@ def lsb_release def lsb return @lsb if defined?(@lsb) @lsb = {} - if !(raw = get_config('/etc/lsb-release')).nil? + if !(raw = read_file('/etc/lsb-release')).nil? @lsb = lsb_config(raw) - elsif unix_file?('/usr/bin/lsb_release') + elsif file_exist?('/usr/bin/lsb_release') @lsb = lsb_release end @lsb end - def detect_linux_lsb + def read_linux_lsb lsb if @lsb.nil? return @lsb unless @lsb[:id].nil? end diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index 8cb44a9b..562c3f25 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -15,19 +15,19 @@ def winrm? @backend.class == Train::Transports::WinRM::Connection end - def get_config(path) + def read_file(path) # keep a log of files incase multiple checks call the same one - @config_files = {} if @config_files.nil? - return @config_files[path] unless @config_files[path].nil? + @files = {} if @files.nil? + return @files[path] unless @files[path].nil? res = @backend.run_command("test -f #{path} && cat #{path}") # ignore files that can't be read return nil if res.exit_status != 0 - @config_files[path] = res.stdout + @files[path] = res.stdout res.stdout end - def unix_file?(path) + def file_exist?(path) @backend.run_command("test -f #{path}").exit_status == 0 end end diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb index 4ffc5dbc..2832ca46 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/os_linux.rb @@ -17,28 +17,5 @@ def redhatish_version(conf) return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i] conf[/release ([\d\.]+)/, 1] end - - def fetch_os_release - data = get_config('/etc/os-release') - return if data.nil? - - os_info = parse_os_release_info(data) - cisco_info_file = os_info['CISCO_RELEASE_INFO'] - if cisco_info_file - os_info.merge!(parse_os_release_info(get_config(cisco_info_file))) - end - - os_info - end - - def parse_os_release_info(raw) - return {} if raw.nil? - - raw.lines.each_with_object({}) do |line, memo| - line.strip! - key, value = line.split('=', 2) - memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty? - end - end end end diff --git a/lib/train/platforms/detect/os_local.rb b/lib/train/platforms/detect/os_local.rb deleted file mode 100644 index 2e0591ab..00000000 --- a/lib/train/platforms/detect/os_local.rb +++ /dev/null @@ -1,38 +0,0 @@ -# encoding: utf-8 - -require 'rbconfig' - -module Train::Platforms::Detect - module OSLocal - def detect_local_os - case ::RbConfig::CONFIG['host_os'] - when /aix(.+)$/ - @platform[:name] = 'aix' - when /darwin(.+)$/ - @platform[:name] = 'darwin' - when /hpux(.+)$/ - @platform[:name] = 'hpux' - when /linux/ - @platform[:name] = 'linux' - when /freebsd(.+)$/ - @platform[:name] = 'freebsd' - when /openbsd(.+)$/ - @platform[:name] = 'openbsd' - when /netbsd(.*)$/ - @platform[:name] = 'netbsd' - when /solaris2/ - @platform[:name] = 'solaris2' - when /mswin|mingw32|windows/ - # After long discussion in IRC the "powers that be" have come to a consensus - # that no Windows platform exists that was not based on the - # Windows_NT kernel, so we herby decree that "windows" will refer to all - # platforms built upon the Windows_NT kernel and have access to win32 or win64 - # subsystems. - @platform[:name] = 'windows' - else - @platform[:name] = ::RbConfig::CONFIG['host_os'] - end - Train::Platforms.list[@platform[:name]] if @platform[:name] - end - end -end diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/specifications/os.rb index f464bf97..34c2ca4f 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -1,43 +1,39 @@ plat = Train::Platforms -# windows platform -plat.name('windows') +plat.family('windows') .detect { - if winrm? - return true if detect_windows == true + require 'rbconfig' + if winrm? || ::RbConfig::CONFIG['host_os'] =~ /mswin|mingw32|windows/ + @platform[:type] = 'windows' + true end } +# windows platform +plat.name('windows').in_family('windows') + .detect { + true if detect_windows == true + } # unix master family plat.family('unix') .detect { - if uname_s =~ /linux/i - @platform[:family] = 'linux' - @platform[:type] = 'unix' - true - elsif uname_s =~ /./ - @platform[:type] = 'unix' - true - end + true if uname_s =~ /./ } # linux master family plat.family('linux').in_family('unix') .detect { - if @platform[:family] == 'linux' - @platform[:arch] = uname_m - true - end + true if uname_s =~ /linux/i } # debian family plat.family('debian').in_family('linux') .detect { - true unless get_config('/etc/debian_version').nil? + true unless read_file('/etc/debian_version').nil? } plat.name('debian').title('Debian Linux').in_family('debian') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /debian/i @platform[:release] = lsb[:release] true @@ -45,7 +41,7 @@ } plat.name('ubuntu').title('Ubuntu Linux').in_family('debian') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /ubuntu/i @platform[:release] = lsb[:release] true @@ -53,7 +49,7 @@ } plat.name('linuxmint').title('LinuxMint').in_family('debian') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /linuxmint/i @platform[:release] = lsb[:release] true @@ -61,8 +57,8 @@ } plat.name('raspbian').title('Raspbian Linux').in_family('debian') .detect { - if unix_file?('/usr/bin/raspi-config') - @platform[:release] = get_config('/etc/debian_version').chomp + if file_exist?('/usr/bin/raspi-config') + @platform[:release] = read_file('/etc/debian_version').chomp true end } @@ -72,29 +68,29 @@ .detect { # I am not sure this returns true for all redhats in this family # for now we are going to just try each platform - # return true unless get_config('/etc/redhat-release').nil? + # return true unless read_file('/etc/redhat-release').nil? true } plat.name('centos').title('Centos Linux').in_family('redhat') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /centos/i @platform[:release] = lsb[:release] true - elsif get_config('/etc/os-release') =~ /centos/i - @platform[:release] = redhatish_version(get_config('/etc/redhat-release')) + elsif read_file('/etc/os-release') =~ /centos/i + @platform[:release] = redhatish_version(read_file('/etc/redhat-release')) true end } # keep redhat after centos as a catchall for redhat base plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /redhat/i @platform[:release] = lsb[:release] true - elsif !(raw = get_config('/etc/redhat-release')).nil? + elsif !(raw = read_file('/etc/redhat-release')).nil? # must be some type of redhat @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) @@ -103,17 +99,17 @@ } plat.name('oracle').title('Oracle Linux').in_family('redhat') .detect { - if !(raw = get_config('/etc/oracle-release')).nil? + if !(raw = read_file('/etc/oracle-release')).nil? @platform[:release] = redhatish_version(raw) true - elsif !(raw = get_config('/etc/enterprise-release')).nil? + elsif !(raw = read_file('/etc/enterprise-release')).nil? @platform[:release] = redhatish_version(raw) true end } plat.name('scientific').title('Scientific Linux').in_family('redhat') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /scientificsl/i @platform[:release] = lsb[:release] true @@ -121,7 +117,7 @@ } plat.name('xenserver').title('Xenserer Linux').in_family('redhat') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /xenserver/i @platform[:release] = lsb[:release] true @@ -129,7 +125,7 @@ } plat.name('parallels-release').title('Parallels Linux').in_family('redhat') .detect { - if !(raw = get_config('/etc/parallels-release')).nil? + if !(raw = read_file('/etc/parallels-release')).nil? @platform[:name] = redhatish_platform(raw) @platform[:release] = raw[/(\d\.\d\.\d)/, 1] true @@ -137,21 +133,21 @@ } plat.name('wrlinux').title('Wind River Linux').in_family('redhat') .detect { - if !(os_info = get_config('/etc/os-release')).nil? + if !(os_info = read_file('/etc/os-release')).nil? if os_info =~ /wrlinux/ @platform[:name] = 'wrlinux' - @platform[:release] = os_info(/VERSION(.*)/) + @platform[:release] = os_info[/VERSION\s?=\s?([\d\.]*)/, 1] true end end } plat.name('amazon').title('Amazon Linux').in_family('redhat') .detect { - lsb = detect_linux_lsb + lsb = read_linux_lsb if lsb && lsb[:id] =~ /amazon/i @platform[:release] = lsb[:release] true - elsif !(raw = get_config('/etc/system-release')).nil? + elsif !(raw = read_file('/etc/system-release')).nil? @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) true @@ -161,7 +157,7 @@ # suse family plat.family('suse').in_family('linux') .detect { - if !(suse = get_config('/etc/SuSE-release')).nil? + if !(suse = read_file('/etc/SuSE-release')).nil? puts suse.inspect version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' @@ -171,17 +167,17 @@ } plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') .detect { - return true if get_config('/etc/SuSE-release') =~ /^openSUSE/ + true if read_file('/etc/SuSE-release') =~ /^openSUSE/ } plat.name('suse').title('Suse Linux').in_family('suse') .detect { - return true if get_config('/etc/SuSE-release') =~ /suse/ + true if read_file('/etc/SuSE-release') =~ /suse/ } # arch plat.name('arch').title('Arch Linux').in_family('linux') .detect { - if !get_config('/etc/arch-release').nil? + if !read_file('/etc/arch-release').nil? # Because this is a rolling release distribution, # use the kernel release, ex. 4.1.6-1-ARCH @platform[:release] = uname_r @@ -192,7 +188,7 @@ # slackware plat.name('slackware').title('Slackware Linux').in_family('linux') .detect { - if !(raw = get_config('/etc/slackware-version')).nil? + if !(raw = read_file('/etc/slackware-version')).nil? @platform[:release] = raw.scan(/(\d+|\.+)/).join true end @@ -201,7 +197,7 @@ # gentoo plat.name('gentoo').title('Gentoo Linux').in_family('linux') .detect { - if !(raw = get_config('/etc/gentoo-release')).nil? + if !(raw = read_file('/etc/gentoo-release')).nil? @platform[:release] = raw.scan(/(\d+|\.+)/).join true end @@ -210,7 +206,7 @@ # exherbo plat.name('exherbo').title('Exherbo Linux').in_family('linux') .detect { - if !(raw = get_config('/etc/exherbo-release')).nil? + if !(raw = read_file('/etc/exherbo-release')).nil? # Because this is a rolling release distribution, # use the kernel release, ex. 4.1.6 @platform[:release] = uname_r @@ -221,7 +217,7 @@ # alpine plat.name('alpine').title('Alpine Linux').in_family('linux') .detect { - if !(raw = get_config('/etc/alpine-release')).nil? + if !(raw = read_file('/etc/alpine-release')).nil? @platform[:release] = raw.strip true end @@ -230,7 +226,7 @@ # coreos plat.name('coreos').title('CoreOS Linux').in_family('linux') .detect { - if !get_config('/etc/coreos/update.conf').nil? + if !read_file('/etc/coreos/update.conf').nil? @platform[:release] = lsb[:release] true end @@ -278,7 +274,7 @@ } plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') .detect { - if unix_file?('/usr/bin/FastCli') + if file_exist?('/usr/bin/FastCli') cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') if cmd.exit_status == 0 && !cmd.stdout.empty? require 'json' @@ -329,14 +325,14 @@ } plat.name('smartos').title('SmartOS').in_family('solaris') .detect { - rel = get_config('/etc/release') + rel = read_file('/etc/release') if /^.*(SmartOS).*$/ =~ rel true end } plat.name('omnios').title('Omnios').in_family('solaris') .detect { - rel = get_config('/etc/release') + rel = read_file('/etc/release') if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil? @platform[:release] = m[2] true @@ -344,7 +340,7 @@ } plat.name('openindiana').title('Openindiana').in_family('solaris') .detect { - rel = get_config('/etc/release') + rel = read_file('/etc/release') if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil? @platform[:release] = m[2] true @@ -352,7 +348,7 @@ } plat.name('opensolaris').title('Open Solaris').in_family('solaris') .detect { - rel = get_config('/etc/release') + rel = read_file('/etc/release') if /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel @platform[:release] = m[2] true @@ -360,14 +356,14 @@ } plat.name('nexentacore').title('Nexentacore').in_family('solaris') .detect { - rel = get_config('/etc/release') + rel = read_file('/etc/release') if /^\s*(NexentaCore)\s.*$/ =~ rel true end } plat.name('solaris').title('Solaris').in_family('solaris') .detect { - rel = get_config('/etc/release') + rel = read_file('/etc/release') if !(m = /Oracle Solaris (\d+)/.match(rel)).nil? # TODO: should be string! @platform[:release] = m[1] diff --git a/lib/train/transports/docker.rb b/lib/train/transports/docker.rb index a5be1004..17d45a39 100644 --- a/lib/train/transports/docker.rb +++ b/lib/train/transports/docker.rb @@ -102,13 +102,5 @@ def uri "docker://#{@container.id}" end end - - class OS < OSCommon - def initialize(backend) - # hardcoded to unix/linux for now, until other operating systems - # are supported - super(backend, { family: 'unix' }) - end - end end end diff --git a/lib/train/transports/mock.rb b/lib/train/transports/mock.rb index 75e59a39..1ac48db0 100644 --- a/lib/train/transports/mock.rb +++ b/lib/train/transports/mock.rb @@ -65,7 +65,7 @@ class Connection < BaseConnection def initialize(conf = nil) super(conf) - @os = OS.new(self, family: 'unknown') + @os = Train::Platforms::Detect.scan(self) @commands = {} end @@ -74,7 +74,15 @@ def uri end def mock_os(value) - @os = OS.new(self, value) + platform + end + + def os + platform + end + + def platform + @platform ||= Train::Platforms::Detect.scan(self) end def mock_command(cmd, stdout = nil, stderr = nil, exit_status = 0) @@ -116,17 +124,17 @@ class Train::Transports::Mock::Connection Command = Struct.new(:stdout, :stderr, :exit_status) end -class Train::Transports::Mock::Connection - class OS < OSCommon - def initialize(backend, desc) - super(backend, desc) - end - - def detect_family - # no op, we do not need to detect the os - end - end -end +# class Train::Transports::Mock::Connection +# class OS < OSCommon +# def initialize(backend, desc) +# super(backend, desc) +# end +# +# def detect_family +# # no op, we do not need to detect the os +# end +# end +# end class Train::Transports::Mock::Connection class File < FileCommon diff --git a/lib/train/transports/winrm_connection.rb b/lib/train/transports/winrm_connection.rb index cd3e7b66..d307a3cf 100644 --- a/lib/train/transports/winrm_connection.rb +++ b/lib/train/transports/winrm_connection.rb @@ -47,7 +47,11 @@ def close end def os - @os ||= OS.new(self) + platform + end + + def platform + @platform ||= Train::Platforms::Detect.scan(self) end def file(path) @@ -193,11 +197,5 @@ def to_s options_to_print[:password] = '' if options_to_print.key?(:password) "#{@username}@#{@hostname}<#{options_to_print.inspect}>" end - - class OS < OSCommon - def initialize(backend) - super(backend, { family: 'windows' }) - end - end end end From 83d4aadb0509ca23c2eb5e592420389f6f76cc73 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Thu, 26 Oct 2017 10:50:48 -0400 Subject: [PATCH 09/22] add unit tests and finalize detect logic Signed-off-by: Jared Quick --- lib/train/platform.rb | 57 ++++- lib/train/platforms.rb | 23 +- lib/train/platforms/detect.rb | 82 +------ lib/train/platforms/detect/linux_lsb.rb | 38 --- lib/train/platforms/detect/os_common.rb | 35 ++- lib/train/platforms/detect/os_linux.rb | 56 ++++- lib/train/platforms/detect/scanner.rb | 87 +++++++ lib/train/platforms/detect/uname.rb | 24 -- lib/train/platforms/family.rb | 15 +- lib/train/platforms/specifications/os.rb | 184 ++++++++------ lib/train/plugins/base_connection.rb | 1 - lib/train/transports/mock.rb | 42 ++-- test/unit/extras/linux_file_test.rb | 4 +- test/unit/extras/os_detect_linux_test.rb | 230 ------------------ test/unit/helper.rb | 1 + test/unit/platforms/detect/common_test.rb | 86 +++++++ test/unit/platforms/detect/linux_test.rb | 124 ++++++++++ test/unit/platforms/detect/scanner_test.rb | 61 +++++ .../detect/windows_test.rb} | 9 +- test/unit/platforms/family_test.rb | 32 +++ test/unit/platforms/os_detect_test.rb | 147 +++++++++++ .../platform_test.rb} | 104 ++++++-- test/unit/platforms/platforms_test.rb | 42 ++++ test/unit/transports/local_file_test.rb | 6 +- test/unit/transports/local_test.rb | 25 +- test/unit/transports/mock_test.rb | 6 +- test/unit/transports/ssh_test.rb | 22 +- 27 files changed, 954 insertions(+), 589 deletions(-) delete mode 100644 lib/train/platforms/detect/linux_lsb.rb create mode 100644 lib/train/platforms/detect/scanner.rb delete mode 100644 lib/train/platforms/detect/uname.rb delete mode 100644 test/unit/extras/os_detect_linux_test.rb create mode 100644 test/unit/platforms/detect/common_test.rb create mode 100644 test/unit/platforms/detect/linux_test.rb create mode 100644 test/unit/platforms/detect/scanner_test.rb rename test/unit/{extras/os_detect_windows_test.rb => platforms/detect/windows_test.rb} (96%) create mode 100644 test/unit/platforms/family_test.rb create mode 100644 test/unit/platforms/os_detect_test.rb rename test/unit/{extras/os_common_test.rb => platforms/platform_test.rb} (76%) create mode 100644 test/unit/platforms/platforms_test.rb diff --git a/lib/train/platform.rb b/lib/train/platform.rb index 3482c43a..165157c0 100644 --- a/lib/train/platform.rb +++ b/lib/train/platform.rb @@ -3,13 +3,16 @@ module Train class Platform include Train::Platforms::Common - attr_accessor :name, :condition, :families, :backend, :platform, :family_hierarchy + attr_accessor :condition, :families, :backend, :platform, :family_hierarchy def initialize(name, condition = {}) - @condition = condition @name = name + @condition = condition @families = {} @family_hierarchy = [] + @platform = {} + @detect = nil + @title = name =~ /^[[:alpha:]]+$/ ? name.capitalize : name # add itself to the platform list Train::Platforms.list[name] = self @@ -19,25 +22,55 @@ def direct_families @families.collect { |k, _v| k.name } end + def name + # Override here incase a updated name was set + # during the detect logic + @platform[:name] || @name + end + # This is for backwords compatability with # the current inspec os resource. def[](name) send(name) end - define_method "#{name}?" do |_arg = nil| - true + def title(title = nil) + return @title if title.nil? + @title = title + self end - def title(title = nil) - if @title.nil? && title.nil? - name.capitalize - elsif title.nil? - @title - else - @title = title - self + def to_hash + @platform + end + + # Add genaric family? and platform methods to an existing platform + # + # This is done later to add any custom + # families/properties that were created + def add_platform_methods + family_list = Train::Platforms.families + family_list.each_value do |k| + next if respond_to?(k.name + '?') + define_singleton_method(k.name + '?') { + family_hierarchy.include?(k.name) + } end + + # Helper methods for direct platform info + @platform.each_key do |m| + next if respond_to?(m) + define_singleton_method(m) { + @platform[m] + } + end + + # Create method for name if its not already true + plat_name = name.downcase.tr(' ', '_') + '?' + return if respond_to?(plat_name) + define_singleton_method(plat_name) { + true + } end end end diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 1e3c927e..e7608fb0 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -60,32 +60,11 @@ def self.top_platforms top_platforms end - # Add genaric family? and platform methods to an existing platform - # - # This is done later to add any custom - # families/properties that were created - def self.add_platform_methods(plat) - families.each_value do |k| - plat.class.send(:define_method, k.name + '?') { - plat.family_hierarchy.include?(k.name) - } - end - - # Helper methods for direct platform info - # Note: this can override platform.name if a - # custom one was found during detect block - plat.platform.each_key do |m| - plat.class.send(:define_method, m) { - @platform[m] - } - end - end - # List all platforms and families in a readable output def self.list_all top_platforms = self.top_platforms top_platforms.each_value do |platform| - puts "#{platform.title} (#{platform.class})" + puts platform.title print_children(platform) if defined?(platform.children) end end diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index deba7db8..02aada92 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -1,78 +1,14 @@ # encoding: utf-8 -require 'train/platforms/specifications/os' -require 'train/platforms/detect/os_common' - -module Train::Platforms::Detect - extend Train::Platforms::Detect::OSCommon - - # Main detect method to scan all platforms for a match - # - # @return Train::Platform instance or error if none found - def self.scan(backend) - @backend = backend - @platform = {} - @family_hierarchy = [] - - # start with the platform/families who have no families (the top levels) - top = Train::Platforms.top_platforms - top.each do |name, plat| - puts "---> Testing-TOP: #{name} - #{plat.class}" - next unless plat.detect - result = instance_eval(&plat.detect) - next unless result == true - - # if we have a match start looking at the children - plat_result = scan_children(plat) - next if plat_result.nil? - - # return platform to backend - @family_hierarchy << plat.name - plat_result.family_hierarchy = @family_hierarchy - return plat_result - end - - raise Train::PlatformDetectionFailed, 'Sorry we did not find your platform' - end - - def self.scan_children(parent) - parent.children.each do |plat, condition| - next if plat.detect.nil? - puts "---> Testing: #{plat.name} - #{plat.class}" - result = instance_eval(&plat.detect) - next unless result == true - - if plat.class == Train::Platform - @platform[:family] = parent.name - return get_platform(plat) if condition.empty? || condition_check(condition) - elsif plat.class == Train::Platforms::Family - plat = scan_family_children(plat) - return plat unless plat.nil? - end +require 'train/platforms/detect/scanner' + +module Train::Platforms + module Detect + # Main detect method to scan all platforms for a match + # + # @return Train::Platform instance or error if none found + def self.scan(backend) + Scanner.new(backend).scan end - - nil - end - - def self.scan_family_children(plat) - child_result = scan_children(plat) if !plat.children.nil? - return if child_result.nil? - @family_hierarchy << plat.name - child_result - end - - def self.check_condition(condition) - condition.each do |k, v| - return false unless instance_eval("#{@platform[k]} #{v}") - end - - true - end - - def self.get_platform(plat) - plat.backend = @backend - plat.platform = @platform - Train::Platforms.add_platform_methods(plat) - plat end end diff --git a/lib/train/platforms/detect/linux_lsb.rb b/lib/train/platforms/detect/linux_lsb.rb deleted file mode 100644 index 80da3e1f..00000000 --- a/lib/train/platforms/detect/linux_lsb.rb +++ /dev/null @@ -1,38 +0,0 @@ -# encoding: utf-8 - -module Train::Platforms::Detect - module LinuxLSB - def lsb_config(content) - { - id: content[/^DISTRIB_ID=["']?(.+?)["']?$/, 1], - release: content[/^DISTRIB_RELEASE=["']?(.+?)["']?$/, 1], - codename: content[/^DISTRIB_CODENAME=["']?(.+?)["']?$/, 1], - } - end - - def lsb_release - raw = @backend.run_command('lsb_release -a').stdout - { - id: raw[/^Distributor ID:\s+(.+)$/, 1], - release: raw[/^Release:\s+(.+)$/, 1], - codename: raw[/^Codename:\s+(.+)$/, 1], - } - end - - def lsb - return @lsb if defined?(@lsb) - @lsb = {} - if !(raw = read_file('/etc/lsb-release')).nil? - @lsb = lsb_config(raw) - elsif file_exist?('/usr/bin/lsb_release') - @lsb = lsb_release - end - @lsb - end - - def read_linux_lsb - lsb if @lsb.nil? - return @lsb unless @lsb[:id].nil? - end - end -end diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index 562c3f25..b403cdcf 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -2,33 +2,54 @@ require 'train/platforms/detect/os_linux' require 'train/platforms/detect/os_windows' -require 'train/platforms/detect/linux_lsb' +require 'rbconfig' module Train::Platforms::Detect module OSCommon include Train::Platforms::Detect::Linux - include Train::Platforms::Detect::LinuxLSB include Train::Platforms::Detect::Windows + def rbconfig(regex) + ::RbConfig::CONFIG['host_os'] =~ regex + end + def winrm? Object.const_defined?('Train::Transports::WinRM::Connection') && @backend.class == Train::Transports::WinRM::Connection end - def read_file(path) + def unix_file_contents(path) # keep a log of files incase multiple checks call the same one - @files = {} if @files.nil? return @files[path] unless @files[path].nil? res = @backend.run_command("test -f #{path} && cat #{path}") + @files[path] = res.stdout # ignore files that can't be read return nil if res.exit_status != 0 - @files[path] = res.stdout res.stdout end - def file_exist?(path) - @backend.run_command("test -f #{path}").exit_status == 0 + def unix_file_exist?(path) + @backend.run_command("test -f #{path}").exit_status.zero? + end + + def unix_uname_s + return @uname[:s] unless @uname[:s].nil? + @uname[:s] = @backend.run_command('uname -s').stdout + end + + def unix_uname_r + return @uname[:r] unless @uname[:r].nil? + @uname[:r] = begin + res = @backend.run_command('uname -r').stdout + res.strip! unless res.nil? + res + end + end + + def unix_uname_m + return @uname[:m] unless @uname[:m].nil? + @uname[:m] = @backend.run_command('uname -m').stdout.chomp end end end diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb index 2832ca46..467e91ac 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/os_linux.rb @@ -1,13 +1,7 @@ # encoding: utf-8 -require 'train/platforms/detect/linux_lsb' -require 'train/platforms/detect/uname' - module Train::Platforms::Detect - module Linux # rubocop:disable Metrics/ModuleLength - include Train::Platforms::Detect::LinuxLSB - include Train::Platforms::Detect::Uname - + module Linux def redhatish_platform(conf) conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase end @@ -17,5 +11,53 @@ def redhatish_version(conf) return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i] conf[/release ([\d\.]+)/, 1] end + + def linux_os_release + data = unix_file_contents('/etc/os-release') + return if data.nil? + + os_info = parse_os_release_info(data) + cisco_info_file = os_info['CISCO_RELEASE_INFO'] + if cisco_info_file + os_info.merge!(parse_os_release_info(unix_file_contents(cisco_info_file))) + end + + os_info + end + + def parse_os_release_info(raw) + return {} if raw.nil? + + raw.lines.each_with_object({}) do |line, memo| + line.strip! + key, value = line.split('=', 2) + memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty? + end + end + + def lsb_config(content) + { + id: content[/^DISTRIB_ID=["']?(.+?)["']?$/, 1], + release: content[/^DISTRIB_RELEASE=["']?(.+?)["']?$/, 1], + codename: content[/^DISTRIB_CODENAME=["']?(.+?)["']?$/, 1], + } + end + + def lsb_release(content) + { + id: content[/^Distributor ID:\s+(.+)$/, 1], + release: content[/^Release:\s+(.+)$/, 1], + codename: content[/^Codename:\s+(.+)$/, 1], + } + end + + def read_linux_lsb + return @lsb unless @lsb.empty? + if !(raw = unix_file_contents('/etc/lsb-release')).nil? + @lsb = lsb_config(raw) + elsif !(raw = unix_file_contents('/usr/bin/lsb-release')).nil? + @lsb = lsb_release(raw) + end + end end end diff --git a/lib/train/platforms/detect/scanner.rb b/lib/train/platforms/detect/scanner.rb new file mode 100644 index 00000000..8c9c5b0c --- /dev/null +++ b/lib/train/platforms/detect/scanner.rb @@ -0,0 +1,87 @@ +# encoding: utf-8 + +require 'train/platforms/specifications/os' +require 'train/platforms/detect/os_common' + +module Train::Platforms::Detect + class Scanner + include Train::Platforms::Detect::OSCommon + + def initialize(backend) + @backend = backend + @platform = {} + @family_hierarchy = [] + + # detect cache variables + @files = {} + @uname = {} + @lsb = {} + end + + # Main detect method to scan all platforms for a match + # + # @return Train::Platform instance or error if none found + def scan + # start with the platform/families who have no families (the top levels) + top = Train::Platforms.top_platforms + top.each do |name, plat| + next unless plat.detect + result = instance_eval(&plat.detect) + next unless result == true + + # if we have a match start looking at the children + plat_result = scan_children(plat) + next if plat_result.nil? + + # return platform to backend + @family_hierarchy << plat.name + return get_platform(plat_result) + end + + raise Train::PlatformDetectionFailed, 'Sorry we did not find your platform' + end + + def scan_children(parent) + parent.children.each do |plat, condition| + next if plat.detect.nil? + result = instance_eval(&plat.detect) + next unless result == true + + if plat.class == Train::Platform + @platform[:family] = parent.name + return plat if condition.empty? || check_condition(condition) + elsif plat.class == Train::Platforms::Family + plat = scan_family_children(plat) + return plat unless plat.nil? + end + end + + nil + end + + def scan_family_children(plat) + child_result = scan_children(plat) if !plat.children.nil? + return if child_result.nil? + @family_hierarchy << plat.name + child_result + end + + def check_condition(condition) + condition.each do |k, v| + opp, expected = v.strip.split(' ') + opp = '==' if opp == '=' + return false if @platform[k].nil? || !instance_eval("'#{@platform[k]}' #{opp} '#{expected}'") + end + + true + end + + def get_platform(plat) + plat.backend = @backend + plat.platform = @platform + plat.add_platform_methods + plat.family_hierarchy = @family_hierarchy + plat + end + end +end diff --git a/lib/train/platforms/detect/uname.rb b/lib/train/platforms/detect/uname.rb deleted file mode 100644 index bc05b29b..00000000 --- a/lib/train/platforms/detect/uname.rb +++ /dev/null @@ -1,24 +0,0 @@ -# encoding: utf-8 - -module Train::Platforms::Detect - module Uname - def uname_s - return @uname_s unless @uname_s.nil? - @uname_s = @backend.run_command('uname -s').stdout - end - - def uname_r - return @uname_r unless @uname_r.nil? - @uname_r = begin - res = @backend.run_command('uname -r').stdout - res.strip! unless res.nil? - res - end - end - - def uname_m - return @uname_m unless @uname_m.nil? - @uname_m = @backend.run_command('uname -m').stdout.chomp - end - end -end diff --git a/lib/train/platforms/family.rb b/lib/train/platforms/family.rb index 7735577d..dc438de5 100644 --- a/lib/train/platforms/family.rb +++ b/lib/train/platforms/family.rb @@ -7,23 +7,20 @@ class Family def initialize(name, condition) @name = name - @condition = {} + @condition = condition @families = {} @children = {} + @detect = nil + @title = name =~ /^[[:alpha:]]+$/ ? "#{name.capitalize} Family" : name # add itself to the families list Train::Platforms.families[@name.to_s] = self end def title(title = nil) - if @title.nil? && title.nil? - "#{name.capitalize} Family" - elsif title.nil? - @title - else - @title = title - self - end + return @title if title.nil? + @title = title + self end end end diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/specifications/os.rb index 34c2ca4f..3554e23d 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -2,8 +2,7 @@ plat.family('windows') .detect { - require 'rbconfig' - if winrm? || ::RbConfig::CONFIG['host_os'] =~ /mswin|mingw32|windows/ + if winrm? || (@backend.local? && rbconfig(/mswin|mingw32|windows/)) @platform[:type] = 'windows' true end @@ -17,27 +16,22 @@ # unix master family plat.family('unix') .detect { - true if uname_s =~ /./ + if unix_uname_s =~ /./ + @platform[:arch] = unix_uname_m + true + end } # linux master family plat.family('linux').in_family('unix') .detect { - true if uname_s =~ /linux/i + true if unix_uname_s =~ /linux/i } # debian family plat.family('debian').in_family('linux') .detect { - true unless read_file('/etc/debian_version').nil? - } -plat.name('debian').title('Debian Linux').in_family('debian') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /debian/i - @platform[:release] = lsb[:release] - true - end + true unless unix_file_contents('/etc/debian_version').nil? } plat.name('ubuntu').title('Ubuntu Linux').in_family('debian') .detect { @@ -57,18 +51,40 @@ } plat.name('raspbian').title('Raspbian Linux').in_family('debian') .detect { - if file_exist?('/usr/bin/raspi-config') - @platform[:release] = read_file('/etc/debian_version').chomp + if unix_file_exist?('/usr/bin/raspi-config') + @platform[:release] = unix_file_contents('/etc/debian_version').chomp true end } +plat.name('debian').title('Debian Linux').in_family('debian') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /debian/i + @platform[:release] = lsb[:release] + true + end + + # if we get this far we have to be some type of debian + true + } + +# fedora family +plat.family('fedora').in_family('linux') + .detect { + true if linux_os_release && linux_os_release['NAME'] =~ /fedora/i + } +plat.name('fedora').title('Fedora').in_family('fedora') + .detect { + @platform[:release] = linux_os_release['VERSION_ID'] + true + } # redhat family plat.family('redhat').in_family('linux') .detect { # I am not sure this returns true for all redhats in this family # for now we are going to just try each platform - # return true unless read_file('/etc/redhat-release').nil? + # return true unless unix_file_contents('/etc/redhat-release').nil? true } @@ -78,8 +94,8 @@ if lsb && lsb[:id] =~ /centos/i @platform[:release] = lsb[:release] true - elsif read_file('/etc/os-release') =~ /centos/i - @platform[:release] = redhatish_version(read_file('/etc/redhat-release')) + elsif unix_file_contents('/etc/os-release') =~ /centos/i + @platform[:release] = redhatish_version(unix_file_contents('/etc/redhat-release')) true end } @@ -90,7 +106,7 @@ if lsb && lsb[:id] =~ /redhat/i @platform[:release] = lsb[:release] true - elsif !(raw = read_file('/etc/redhat-release')).nil? + elsif !(raw = unix_file_contents('/etc/redhat-release')).nil? # must be some type of redhat @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) @@ -99,10 +115,10 @@ } plat.name('oracle').title('Oracle Linux').in_family('redhat') .detect { - if !(raw = read_file('/etc/oracle-release')).nil? + if !(raw = unix_file_contents('/etc/oracle-release')).nil? @platform[:release] = redhatish_version(raw) true - elsif !(raw = read_file('/etc/enterprise-release')).nil? + elsif !(raw = unix_file_contents('/etc/enterprise-release')).nil? @platform[:release] = redhatish_version(raw) true end @@ -125,7 +141,7 @@ } plat.name('parallels-release').title('Parallels Linux').in_family('redhat') .detect { - if !(raw = read_file('/etc/parallels-release')).nil? + if !(raw = unix_file_contents('/etc/parallels-release')).nil? @platform[:name] = redhatish_platform(raw) @platform[:release] = raw[/(\d\.\d\.\d)/, 1] true @@ -133,10 +149,9 @@ } plat.name('wrlinux').title('Wind River Linux').in_family('redhat') .detect { - if !(os_info = read_file('/etc/os-release')).nil? - if os_info =~ /wrlinux/ - @platform[:name] = 'wrlinux' - @platform[:release] = os_info[/VERSION\s?=\s?([\d\.]*)/, 1] + unless linux_os_release.nil? + if linux_os_release['ID_LIKE'] =~ /wrlinux/i + @platform[:release] = linux_os_release['VERSION'] true end end @@ -147,7 +162,7 @@ if lsb && lsb[:id] =~ /amazon/i @platform[:release] = lsb[:release] true - elsif !(raw = read_file('/etc/system-release')).nil? + elsif !(raw = unix_file_contents('/etc/system-release')).nil? @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) true @@ -157,7 +172,7 @@ # suse family plat.family('suse').in_family('linux') .detect { - if !(suse = read_file('/etc/SuSE-release')).nil? + if !(suse = unix_file_contents('/etc/SuSE-release')).nil? puts suse.inspect version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' @@ -167,20 +182,20 @@ } plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') .detect { - true if read_file('/etc/SuSE-release') =~ /^openSUSE/ + true if unix_file_contents('/etc/SuSE-release') =~ /^openSUSE/ } plat.name('suse').title('Suse Linux').in_family('suse') .detect { - true if read_file('/etc/SuSE-release') =~ /suse/ + true if unix_file_contents('/etc/SuSE-release') =~ /suse/ } # arch plat.name('arch').title('Arch Linux').in_family('linux') .detect { - if !read_file('/etc/arch-release').nil? + if !unix_file_contents('/etc/arch-release').nil? # Because this is a rolling release distribution, # use the kernel release, ex. 4.1.6-1-ARCH - @platform[:release] = uname_r + @platform[:release] = unix_uname_r true end } @@ -188,7 +203,7 @@ # slackware plat.name('slackware').title('Slackware Linux').in_family('linux') .detect { - if !(raw = read_file('/etc/slackware-version')).nil? + if !(raw = unix_file_contents('/etc/slackware-version')).nil? @platform[:release] = raw.scan(/(\d+|\.+)/).join true end @@ -197,7 +212,7 @@ # gentoo plat.name('gentoo').title('Gentoo Linux').in_family('linux') .detect { - if !(raw = read_file('/etc/gentoo-release')).nil? + if !(raw = unix_file_contents('/etc/gentoo-release')).nil? @platform[:release] = raw.scan(/(\d+|\.+)/).join true end @@ -206,10 +221,10 @@ # exherbo plat.name('exherbo').title('Exherbo Linux').in_family('linux') .detect { - if !(raw = read_file('/etc/exherbo-release')).nil? + unless unix_file_contents('/etc/exherbo-release').nil? # Because this is a rolling release distribution, # use the kernel release, ex. 4.1.6 - @platform[:release] = uname_r + @platform[:release] = unix_uname_r true end } @@ -217,7 +232,7 @@ # alpine plat.name('alpine').title('Alpine Linux').in_family('linux') .detect { - if !(raw = read_file('/etc/alpine-release')).nil? + if !(raw = unix_file_contents('/etc/alpine-release')).nil? @platform[:release] = raw.strip true end @@ -226,7 +241,8 @@ # coreos plat.name('coreos').title('CoreOS Linux').in_family('linux') .detect { - if !read_file('/etc/coreos/update.conf').nil? + if !unix_file_contents('/etc/coreos/update.conf').nil? + lsb = read_linux_lsb @platform[:release] = lsb[:release] true end @@ -242,7 +258,7 @@ # openvms plat.name('openvms').title('OpenVMS').in_family('unix') .detect { - if uname_s =~ /unrecognized command verb/i + if unix_uname_s =~ /unrecognized command verb/i cmd = @backend.run_command('show system/noprocess') unless cmd.exit_status != 0 || cmd.stdout.empty? @platform[:name] = cmd.stdout.downcase.split(' ')[0] @@ -256,7 +272,7 @@ } # arista_eos family -plat.family('arista_eos').in_family('unix') +plat.family('arista_eos').title('Arista EOS Family').in_family('unix') .detect { # we need a better way to determin this family true @@ -274,7 +290,7 @@ } plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') .detect { - if file_exist?('/usr/bin/FastCli') + if unix_file_exist?('/usr/bin/FastCli') cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') if cmd.exit_status == 0 && !cmd.stdout.empty? require 'json' @@ -287,33 +303,37 @@ } # esx -plat.name('esx').title('ESX').in_family('unix') +plat.family('esx').title('ESXi Family') .detect { - if uname_s =~ /vmkernel/i - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp - true - end + true if unix_uname_s =~ /vmkernel/i + } +plat.name('vmkernel').in_family('esx') + .detect { + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp + true } # aix -plat.name('aix').title('Aix').in_family('unix') - .detect { - if uname_s =~ /aix/ - out = @backend.run_command('uname -rvp').stdout - m = out.match(/(\d+)\s+(\d+)\s+(.*)/) - unless m.nil? - @platform[:release] = "#{m[2]}.#{m[1]}" - @platform[:arch] = m[3].to_s - end - true +plat.family('aix').in_family('unix') + .detect { + true if unix_uname_s =~ /aix/ + } +plat.name('aix').title('Aix').in_family('aix') + .detect { + out = @backend.run_command('uname -rvp').stdout + m = out.match(/(\d+)\s+(\d+)\s+(.*)/) + unless m.nil? + @platform[:release] = "#{m[2]}.#{m[1]}" + @platform[:arch] = m[3].to_s end + true } # solaris family plat.family('solaris').in_family('unix') .detect { - if uname_s =~ /sunos/i + if unix_uname_s =~ /sunos/i unless (version = /^5\.(?\d+)$/.match(uname_r)).nil? @platform[:release] = version['release'] end @@ -325,14 +345,14 @@ } plat.name('smartos').title('SmartOS').in_family('solaris') .detect { - rel = read_file('/etc/release') + rel = unix_file_contents('/etc/release') if /^.*(SmartOS).*$/ =~ rel true end } plat.name('omnios').title('Omnios').in_family('solaris') .detect { - rel = read_file('/etc/release') + rel = unix_file_contents('/etc/release') if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil? @platform[:release] = m[2] true @@ -340,7 +360,7 @@ } plat.name('openindiana').title('Openindiana').in_family('solaris') .detect { - rel = read_file('/etc/release') + rel = unix_file_contents('/etc/release') if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil? @platform[:release] = m[2] true @@ -348,7 +368,7 @@ } plat.name('opensolaris').title('Open Solaris').in_family('solaris') .detect { - rel = read_file('/etc/release') + rel = unix_file_contents('/etc/release') if /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel @platform[:release] = m[2] true @@ -356,14 +376,14 @@ } plat.name('nexentacore').title('Nexentacore').in_family('solaris') .detect { - rel = read_file('/etc/release') + rel = unix_file_contents('/etc/release') if /^\s*(NexentaCore)\s.*$/ =~ rel true end } plat.name('solaris').title('Solaris').in_family('solaris') .detect { - rel = read_file('/etc/release') + rel = unix_file_contents('/etc/release') if !(m = /Oracle Solaris (\d+)/.match(rel)).nil? # TODO: should be string! @platform[:release] = m[1] @@ -377,12 +397,14 @@ } # hpux -plat.name('hpux').title('Hpux').in_family('unix') +plat.family('hpux').in_family('unix') .detect { - if uname_s =~ /hp-ux/ - @platform[:release] = uname_r.lines[0].chomp + true if unix_uname_s =~ /hp-ux/ + } +plat.name('hpux').title('Hpux').in_family('hpux') + .detect { + @platform[:release] = unix_uname_r.lines[0].chomp true - end } # bsd family @@ -392,9 +414,9 @@ } plat.name('darwin').title('Darwin').in_family('bsd') .detect { - if uname_s =~ /darwin/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp + if unix_uname_s =~ /darwin/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp true end cmd = @backend.run_command('/usr/bin/sw_vers') @@ -402,30 +424,30 @@ @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1] @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1] - @platform[:arch] = uname_m + @platform[:arch] = unix_uname_m true } plat.name('freebsd').title('Freebsd').in_family('bsd') .detect { - if uname_s =~ /freebsd/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp + if unix_uname_s =~ /freebsd/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp true end } plat.name('openbsd').title('Openbsd').in_family('bsd') .detect { - if uname_s =~ /openbsd/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp + if unix_uname_s =~ /openbsd/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp true end } plat.name('netbsd').title('Netbsd').in_family('bsd') .detect { - if uname_s =~ /netbsd/ - @platform[:name] = uname_s.lines[0].chomp - @platform[:release] = uname_r.lines[0].chomp + if unix_uname_s =~ /netbsd/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp true end } diff --git a/lib/train/plugins/base_connection.rb b/lib/train/plugins/base_connection.rb index 05c9dc32..91bc8300 100644 --- a/lib/train/plugins/base_connection.rb +++ b/lib/train/plugins/base_connection.rb @@ -28,7 +28,6 @@ class BaseConnection def initialize(options = nil) @options = options || {} @logger = @options.delete(:logger) || Logger.new(STDOUT) - @logger.level = Logger::WARN @files = {} end diff --git a/lib/train/transports/mock.rb b/lib/train/transports/mock.rb index 1ac48db0..23928af4 100644 --- a/lib/train/transports/mock.rb +++ b/lib/train/transports/mock.rb @@ -1,7 +1,4 @@ # encoding: utf-8 -# -# author: Dominik Richter -# author: Christoph Hartmann require 'train/plugins' require 'digest' @@ -34,7 +31,7 @@ def trace_calls 'Train::Transports::Mock::Connection::File' => Connection::FileCommon.instance_methods(false), 'Train::Transports::Mock::Connection::OS' => - Connection::OSCommon.instance_methods(false), + Train::Platform.instance_methods(false), } # rubocop:disable Metrics/ParameterLists @@ -65,7 +62,7 @@ class Connection < BaseConnection def initialize(conf = nil) super(conf) - @os = Train::Platforms::Detect.scan(self) + @os = mock_os({}) @commands = {} end @@ -74,15 +71,20 @@ def uri end def mock_os(value) - platform - end - - def os - platform - end - - def platform - @platform ||= Train::Platforms::Detect.scan(self) + value[:name] = 'unknown' unless value[:name] + platform = Train::Platforms.name(value[:name]) + platform.family_hierarchy = mock_os_hierarchy(platform).flatten + platform.platform[:family] = platform.family_hierarchy[0] + platform.add_platform_methods + @os = platform + @os + end + + def mock_os_hierarchy(plat) + plat.families.each_with_object([]) do |(k, _v), memo| + memo << k.name + memo << mock_os_hierarchy(k) unless k.families.empty? + end end def mock_command(cmd, stdout = nil, stderr = nil, exit_status = 0) @@ -124,18 +126,6 @@ class Train::Transports::Mock::Connection Command = Struct.new(:stdout, :stderr, :exit_status) end -# class Train::Transports::Mock::Connection -# class OS < OSCommon -# def initialize(backend, desc) -# super(backend, desc) -# end -# -# def detect_family -# # no op, we do not need to detect the os -# end -# end -# end - class Train::Transports::Mock::Connection class File < FileCommon def self.from_json(json) diff --git a/test/unit/extras/linux_file_test.rb b/test/unit/extras/linux_file_test.rb index b09074bf..88195cf5 100644 --- a/test/unit/extras/linux_file_test.rb +++ b/test/unit/extras/linux_file_test.rb @@ -7,14 +7,14 @@ let(:cls) { Train::Extras::LinuxFile } let(:backend) { backend = Train::Transports::Mock.new.connection - backend.mock_os({ family: 'linux' }) + backend.mock_os({ name: 'linux', family: 'unix' }) backend } def mock_stat(args, out, err = '', code = 0) backend.mock_command( "stat #{args} 2>/dev/null --printf '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'", - out, err, code, + out, err, code ) end diff --git a/test/unit/extras/os_detect_linux_test.rb b/test/unit/extras/os_detect_linux_test.rb deleted file mode 100644 index fdf99317..00000000 --- a/test/unit/extras/os_detect_linux_test.rb +++ /dev/null @@ -1,230 +0,0 @@ -# encoding: utf-8 -require 'helper' -require 'train/extras' - -class OsDetectLinuxTester - attr_reader :platform - include Train::Extras::DetectLinux - - def initialize - @platform = {} - end -end - -describe 'os_detect_linux' do - let(:detector) { OsDetectLinuxTester.new } - - describe '#detect_linux_arch' do - it "sets the arch using uname" do - be = mock("Backend") - detector.stubs(:backend).returns(be) - be.stubs(:run_command).with("uname -m").returns(mock("Output", stdout: "x86_64\n")) - detector.detect_linux_arch - detector.platform[:arch].must_equal("x86_64") - end - end - - describe '#detect_linux_via_config' do - - before do - detector.stubs(:get_config) - detector.stubs(:fetch_os_release) - detector.stubs(:redhatish_version).returns('redhat-version') - end - - describe '/etc/enterprise-release' do - it 'sets the correct family/release for oracle' do - detector.stubs(:get_config).with('/etc/enterprise-release').returns('data') - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('oracle') - detector.platform[:family].must_equal('redhat') - detector.platform[:release].must_equal('redhat-version') - end - end - - describe "/etc/redhat-release" do - describe "and /etc/os-release" do - it "sets the correct family, name, and release on centos" do - detector.stubs(:get_config).with("/etc/redhat-release").returns("CentOS Linux release 7.2.1511 (Core) \n") - detector.stubs(:get_config).with("/etc/os-release").returns("NAME=\"CentOS Linux\"\nVERSION=\"7 (Core)\"\nID=\"centos\"\nID_LIKE=\"rhel fedora\"\n") - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('centos') - detector.platform[:family].must_equal('redhat') - detector.platform[:release].must_equal('redhat-version') - end - end - end - - describe '/etc/debian_version' do - - before { detector.stubs(:get_config).with('/etc/debian_version').returns('deb-version') } - - describe 'ubuntu' do - it 'sets the correct family/release for ubuntu' do - detector.stubs(:lsb).returns({ id: 'ubuntu', release: 'ubuntu-release' }) - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('ubuntu') - detector.platform[:family].must_equal('debian') - detector.platform[:release].must_equal('ubuntu-release') - end - end - - describe 'linuxmint' do - it 'sets the correct family/release for ubuntu' do - detector.stubs(:lsb).returns({ id: 'linuxmint', release: 'mint-release' }) - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('linuxmint') - detector.platform[:family].must_equal('debian') - detector.platform[:release].must_equal('mint-release') - end - end - - describe 'raspbian' do - it 'sets the correct family/release for raspbian ' do - detector.stubs(:lsb).returns({ id: 'something_else', release: 'some_release' }) - detector.expects(:unix_file?).with('/usr/bin/raspi-config').returns(true) - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('raspbian') - detector.platform[:family].must_equal('debian') - detector.platform[:release].must_equal('deb-version') - end - end - - describe 'everything else' do - it 'sets the correct family/release for debian ' do - detector.stubs(:lsb).returns({ id: 'something_else', release: 'some_release' }) - detector.expects(:unix_file?).with('/usr/bin/raspi-config').returns(false) - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('debian') - detector.platform[:family].must_equal('debian') - detector.platform[:release].must_equal('deb-version') - end - end - end - - describe '/etc/coreos/update.conf' do - it 'sets the correct family/release for coreos' do - detector.stubs(:get_config).with('/etc/coreos/update.conf').returns('data') - detector.stubs(:lsb).returns({ id: 'Container Linux by CoreOS', release: 'coreos-version' }) - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('coreos') - detector.platform[:family].must_equal('coreos') - detector.platform[:release].must_equal('coreos-version') - end - end - - describe '/etc/os-release' do - describe 'when not on a wrlinux build' do - it 'does not set a platform family/release' do - detector.stubs(:fetch_os_release).returns({ 'ID_LIKE' => 'something_else' }) - - detector.detect_linux_via_config.must_equal(false) - detector.platform[:family].must_equal(nil) - detector.platform[:release].must_equal(nil) - end - end - - describe 'when on a wrlinux build' do - let(:data) do - { - 'ID_LIKE' => 'cisco-wrlinux', - 'VERSION' => 'cisco123' - } - end - - it 'sets the correct family/release for wrlinux' do - detector.stubs(:fetch_os_release).returns(data) - - detector.detect_linux_via_config.must_equal(true) - detector.platform[:name].must_equal('wrlinux') - detector.platform[:family].must_equal('redhat') - detector.platform[:release].must_equal('cisco123') - end - end - end - end - - describe '#fetch_os_release' do - describe 'when no os-release data is available' do - it 'returns nil' do - detector.expects(:get_config).with('/etc/os-release').returns(nil) - detector.fetch_os_release.must_equal(nil) - end - end - - describe 'when os-release data exists with no CISCO_RELEASE_INFO' do - let(:os_release) { { 'KEY1' => 'VALUE1' } } - - it 'returns a correct hash' do - detector.expects(:get_config).with('/etc/os-release').returns('os-release data') - detector.expects(:parse_os_release_info).with('os-release data').returns(os_release) - detector.fetch_os_release['KEY1'].must_equal('VALUE1') - end - end - - describe 'when os-release data exists with CISCO_RELEASE_INFO' do - let(:os_release) { { 'KEY1' => 'VALUE1', 'CISCO_RELEASE_INFO' => 'cisco_file' } } - let(:cisco_release) { { 'KEY1' => 'NEWVALUE1', 'KEY2' => 'VALUE2' } } - - it 'returns a correct hash' do - detector.expects(:get_config).with('/etc/os-release').returns('os-release data') - detector.expects(:get_config).with('cisco_file').returns('cisco data') - detector.expects(:parse_os_release_info).with('os-release data').returns(os_release) - detector.expects(:parse_os_release_info).with('cisco data').returns(cisco_release) - - os_info = detector.fetch_os_release - os_info['KEY1'].must_equal('NEWVALUE1') - os_info['KEY2'].must_equal('VALUE2') - end - end - end - - describe '#parse_os_release_info' do - describe 'when nil is supplied' do - it 'returns an empty hash' do - detector.parse_os_release_info(nil).must_equal({}) - end - end - - describe 'when unexpectedly-formatted data is supplied' do - let(:data) do - <<-EOL -blah blah -no good data here - EOL - end - - it 'returns an empty hash' do - detector.parse_os_release_info(nil).must_equal({}) - end - end - - describe 'when properly-formatted data is supplied' do - let(:data) do - <<-EOL -KEY1=value1 -KEY2= -KEY3=value3 -KEY4="value4 with spaces" -KEY5="value5 with a = sign" - EOL - end - - it 'parses the data correctly' do - parsed_data = detector.parse_os_release_info(data) - - parsed_data['KEY1'].must_equal('value1') - parsed_data.key?('KEY2').must_equal(false) - parsed_data['KEY3'].must_equal('value3') - parsed_data['KEY4'].must_equal('value4 with spaces') - parsed_data['KEY5'].must_equal('value5 with a = sign') - end - end - end -end diff --git a/test/unit/helper.rb b/test/unit/helper.rb index 971f5bf1..40694c78 100644 --- a/test/unit/helper.rb +++ b/test/unit/helper.rb @@ -5,3 +5,4 @@ require 'mocha/setup' require 'train' +require 'train/platforms/detect' diff --git a/test/unit/platforms/detect/common_test.rb b/test/unit/platforms/detect/common_test.rb new file mode 100644 index 00000000..b3fb71ae --- /dev/null +++ b/test/unit/platforms/detect/common_test.rb @@ -0,0 +1,86 @@ +# encoding: utf-8 + +require 'helper' + +class OsDetectLinuxTester + attr_reader :platform + include Train::Platforms::Detect::OSCommon + + def initialize + @platform = {} + end +end + +describe 'os_common' do + let(:detector) { OsDetectLinuxTester.new } + + describe 'winrm? check' do + it 'return winrm? true' do + require 'train/transports/winrm' + be = Train::Transports::WinRM::Connection.new(nil) + detector.instance_variable_set(:@backend, be) + detector.winrm?.must_equal(true) + end + + it 'return winrm? false' do + be = mock('Backend') + detector.instance_variable_set(:@backend, be) + detector.winrm?.must_equal(false) + end + end + + describe 'unix file contents' do + it 'return new file contents' do + be = mock('Backend') + output = mock('Output', exit_status: 0) + output.expects(:stdout).twice.returns('test') + be.stubs(:run_command).with('test -f /etc/fstab && cat /etc/fstab').returns(output) + detector.instance_variable_set(:@backend, be) + detector.instance_variable_set(:@files, {}) + detector.unix_file_contents('/etc/fstab').must_equal('test') + # puts detector.instance_variable_get(:@files).inspect + end + + it 'return new file contents cached' do + be = mock('Backend') + detector.instance_variable_set(:@backend, be) + detector.instance_variable_set(:@files, { '/etc/profile' => 'test' }) + detector.unix_file_contents('/etc/profile').must_equal('test') + end + end + + describe 'unix file exist?' do + it 'file does exist' do + be = mock('Backend') + be.stubs(:run_command).with('test -f /etc/test').returns(mock('Output', exit_status: 0)) + detector.instance_variable_set(:@backend, be) + detector.unix_file_exist?('/etc/test').must_equal(true) + end + end + + describe '#detect_linux_arch' do + it 'uname m call' do + be = mock('Backend') + be.stubs(:run_command).with('uname -m').returns(mock('Output', stdout: "x86_64\n")) + detector.instance_variable_set(:@backend, be) + detector.instance_variable_set(:@uname, {}) + detector.unix_uname_m.must_equal('x86_64') + end + + it 'uname s call' do + be = mock('Backend') + be.stubs(:run_command).with('uname -s').returns(mock('Output', stdout: "linux")) + detector.instance_variable_set(:@backend, be) + detector.instance_variable_set(:@uname, {}) + detector.unix_uname_s.must_equal('linux') + end + + it 'uname r call' do + be = mock('Backend') + be.stubs(:run_command).with('uname -r').returns(mock('Output', stdout: "17.0.0\n")) + detector.instance_variable_set(:@backend, be) + detector.instance_variable_set(:@uname, {}) + detector.unix_uname_r.must_equal('17.0.0') + end + end +end diff --git a/test/unit/platforms/detect/linux_test.rb b/test/unit/platforms/detect/linux_test.rb new file mode 100644 index 00000000..5f41cc63 --- /dev/null +++ b/test/unit/platforms/detect/linux_test.rb @@ -0,0 +1,124 @@ +# encoding: utf-8 + +require 'helper' +require 'train/transports/mock' + +class OsDetectLinuxTester + include Train::Platforms::Detect::OSCommon +end + +describe 'os_linux' do + let(:detector) { OsDetectLinuxTester.new } + + describe 'redhatish_platform cleaner' do + it 'normal redhat' do + detector.redhatish_platform('Red Hattter').must_equal('redhat') + end + + it 'custom redhat' do + detector.redhatish_platform('Centos Pro 11').must_equal('centos') + end + end + + describe 'redhatish_version cleaner' do + it 'normal rawhide' do + detector.redhatish_version('18 (Rawhide) Pro').must_equal('18 (rawhide)') + end + + it 'normal linux' do + detector.redhatish_version('derived from Ubuntu Linux 11').must_equal('11') + end + end + + describe 'lsb parse' do + it 'lsb config' do + lsb = "DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=14.06\nDISTRIB_CODENAME=xenial" + expect = { :id=>'Ubuntu', :release=>'14.06', :codename=>'xenial' } + detector.lsb_config(lsb).must_equal(expect) + end + + it 'lsb releasel' do + lsb = "Distributor ID: Ubuntu\nRelease: 14.06\nCodename: xenial" + expect = { :id=>'Ubuntu', :release=>'14.06', :codename=>'xenial' } + detector.lsb_release(lsb).must_equal(expect) + end + end + + describe '#linux_os_release' do + describe 'when no os-release data is available' do + it 'returns nil' do + detector.expects(:unix_file_contents).with('/etc/os-release').returns(nil) + detector.linux_os_release.must_be_nil + end + end + end + + describe 'when os-release data exists with no CISCO_RELEASE_INFO' do + let(:os_release) { { 'KEY1' => 'VALUE1' } } + + it 'returns a correct hash' do + detector.expects(:unix_file_contents).with('/etc/os-release').returns('os-release data') + detector.expects(:parse_os_release_info).with('os-release data').returns(os_release) + detector.linux_os_release['KEY1'].must_equal('VALUE1') + end + end + + describe 'when os-release data exists with CISCO_RELEASE_INFO' do + let(:os_release) { { 'KEY1' => 'VALUE1', 'CISCO_RELEASE_INFO' => 'cisco_file' } } + let(:cisco_release) { { 'KEY1' => 'NEWVALUE1', 'KEY2' => 'VALUE2' } } + + it 'returns a correct hash' do + detector.expects(:unix_file_contents).with('/etc/os-release').returns('os-release data') + detector.expects(:unix_file_contents).with('cisco_file').returns('cisco data') + detector.expects(:parse_os_release_info).with('os-release data').returns(os_release) + detector.expects(:parse_os_release_info).with('cisco data').returns(cisco_release) + + os_info = detector.linux_os_release + os_info['KEY1'].must_equal('NEWVALUE1') + os_info['KEY2'].must_equal('VALUE2') + end + end + + describe '#parse_os_release_info' do + describe 'when nil is supplied' do + it 'returns an empty hash' do + detector.parse_os_release_info(nil).must_equal({}) + end + end + + describe 'when unexpectedly-formatted data is supplied' do + let(:data) do + <<-EOL +blah blah +no good data here + EOL + end + + it 'returns an empty hash' do + detector.parse_os_release_info(nil).must_equal({}) + end + end + + describe 'when properly-formatted data is supplied' do + let(:data) do + <<-EOL +KEY1=value1 +KEY2= +KEY3=value3 +KEY4="value4 with spaces" +KEY5="value5 with a = sign" + EOL + end + + it 'parses the data correctly' do + parsed_data = detector.parse_os_release_info(data) + + parsed_data['KEY1'].must_equal('value1') + parsed_data.key?('KEY2').must_equal(false) + parsed_data['KEY3'].must_equal('value3') + parsed_data['KEY4'].must_equal('value4 with spaces') + parsed_data['KEY5'].must_equal('value5 with a = sign') + end + end + end +end diff --git a/test/unit/platforms/detect/scanner_test.rb b/test/unit/platforms/detect/scanner_test.rb new file mode 100644 index 00000000..6c6b29bc --- /dev/null +++ b/test/unit/platforms/detect/scanner_test.rb @@ -0,0 +1,61 @@ +# encoding: utf-8 + +require 'helper' +require 'train/platforms/detect/scanner' +require 'train/transports/mock' + +describe 'scanner' do + let(:backend) { Train::Transports::Mock::Connection.new } + let(:scanner) { Train::Platforms::Detect::Scanner.new(backend) } + + describe 'scan family children' do + it 'return child' do + family = Train::Platforms.family('linux') + scanner.scan_family_children(family).name.must_equal('linux') + scanner.instance_variable_get(:@family_hierarchy).must_equal(['linux']) + end + + it 'return nil' do + family = Train::Platforms.family('fake-fam') + scanner.scan_family_children(family).must_be_nil + scanner.instance_variable_get(:@family_hierarchy).must_be_empty + end + end + + describe 'check condition' do + it 'return true equal' do + scanner.instance_variable_set(:@platform, { arch: 'x86_64' }) + scanner.check_condition({ arch: '= x86_64' }).must_equal(true) + end + + it 'return true greater then' do + scanner.instance_variable_set(:@platform, { release: '8.2' }) + scanner.check_condition({ release: '>= 7' }).must_equal(true) + end + + it 'return false greater then' do + scanner.instance_variable_set(:@platform, { release: '2.2' }) + scanner.check_condition({ release: '> 7' }).must_equal(false) + end + end + + describe 'get platform' do + it 'return empty platform' do + plat = Train::Platforms.name('linux') + plat = scanner.get_platform(plat) + plat.platform.must_equal({}) + plat.backend.must_equal(backend) + plat.family_hierarchy.must_equal([]) + end + + it 'return full platform' do + scanner.instance_variable_set(:@platform, { family: 'linux' }) + scanner.instance_variable_set(:@family_hierarchy, [ 'linux', 'unix' ]) + plat = Train::Platforms.name('linux') + plat = scanner.get_platform(plat) + plat.platform.must_equal({ family: 'linux' }) + plat.backend.must_equal(backend) + plat.family_hierarchy.must_equal([ 'linux', 'unix' ]) + end + end +end diff --git a/test/unit/extras/os_detect_windows_test.rb b/test/unit/platforms/detect/windows_test.rb similarity index 96% rename from test/unit/extras/os_detect_windows_test.rb rename to test/unit/platforms/detect/windows_test.rb index 26a85a83..4052e318 100644 --- a/test/unit/extras/os_detect_windows_test.rb +++ b/test/unit/platforms/detect/windows_test.rb @@ -1,8 +1,11 @@ -require 'train/extras' +# encoding: utf-8 + +require 'helper' +require 'train/transports/mock' class OsDetectWindowsTester attr_reader :platform, :backend - include Train::Extras::DetectWindows + include Train::Platforms::Detect::Windows def initialize @platform = {} @@ -97,7 +100,7 @@ def initialize detector.detect_windows detector.platform[:family].must_equal('windows') detector.platform[:name].must_equal('Windows 4.10.1998') - detector.platform[:arch].must_equal(nil) + detector.platform[:arch].must_be_nil detector.platform[:release].must_equal('4.10.1998') end end diff --git a/test/unit/platforms/family_test.rb b/test/unit/platforms/family_test.rb new file mode 100644 index 00000000..4b81a3bb --- /dev/null +++ b/test/unit/platforms/family_test.rb @@ -0,0 +1,32 @@ +# encoding: utf-8 + +require 'helper' + +describe 'platform family' do + def mock_family(x) + Train::Platforms.families[x] = nil if x == 'mock' + Train::Platforms.family(x) + end + + it 'set family title' do + plat = mock_family('mock') + plat.title.must_equal('Mock Family') + plat.title('The Best Mock Family') + plat.title.must_equal('The Best Mock Family') + end + + it 'set family in a family' do + plat = mock_family('family1') + plat.in_family('family2') + plat.families.keys[0].name.must_equal('family2') + + plat = mock_family('family2') + plat.children.keys[0].name.must_equal('family1') + end + + it 'set family in a family with condition' do + plat = Train::Platforms.family('family4', arch: '= x68_64').in_family('family5') + plat.families.keys[0].name.must_equal('family5') + plat.families.values[0].must_equal({ arch: '= x68_64' }) + end +end diff --git a/test/unit/platforms/os_detect_test.rb b/test/unit/platforms/os_detect_test.rb new file mode 100644 index 00000000..edb4401e --- /dev/null +++ b/test/unit/platforms/os_detect_test.rb @@ -0,0 +1,147 @@ +# encoding: utf-8 +require 'helper' +require 'train/transports/mock' + +class OsDetectLinuxTester + include Train::Platforms::Detect::OSCommon +end + +describe 'os_detect_linux' do + let(:detector) { OsDetectLinuxTester.new } + + def scan_with_files(uname, files) + mock = Train::Transports::Mock::Connection.new + mock.mock_command('uname -s', uname) + files.each do |path, data| + mock.mock_command("test -f #{path}") + mock.mock_command("test -f #{path} && cat #{path}", data) + end + Train::Platforms::Detect.scan(mock) + end + + ## Detect all linux distros + describe '/etc/enterprise-release' do + it 'sets the correct family/release for oracle' do + path = '/etc/enterprise-release' + platform = scan_with_files('linux', { path => 'release 7' }) + + platform[:name].must_equal('oracle') + platform[:family].must_equal('redhat') + platform[:release].must_equal('7') + end + end + + describe '/etc/redhat-release' do + describe 'and /etc/os-release' do + it 'sets the correct family, name, and release on centos' do + files = { + '/etc/redhat-release' => "CentOS Linux release 7.2.1511 (Core) \n", + '/etc/os-release' => "NAME=\"CentOS Linux\"\nVERSION=\"7 (Core)\"\nID=\"centos\"\nID_LIKE=\"rhel fedora\"\n", + } + platform = scan_with_files('linux', files) + platform[:name].must_equal('centos') + platform[:family].must_equal('redhat') + platform[:release].must_equal('7.2.1511') + end + end + end + + describe '/etc/debian_version' do + def debian_scan(id, version) + lsb_release = "DISTRIB_ID=#{id}\nDISTRIB_RELEASE=#{version}" + files = { + '/etc/lsb-release' => lsb_release, + '/etc/debian_version' => 'data', + } + scan_with_files('linux', files) + end + + describe 'ubuntu' do + it 'sets the correct family/release for ubuntu' do + platform = debian_scan('ubuntu', '16.04') + + platform[:name].must_equal('ubuntu') + platform[:family].must_equal('debian') + platform[:release].must_equal('16.04') + end + end + + describe 'linuxmint' do + it 'sets the correct family/release for linuxmint' do + platform = debian_scan('linuxmint', '12') + + platform[:name].must_equal('linuxmint') + platform[:family].must_equal('debian') + platform[:release].must_equal('12') + end + end + + describe 'raspbian' do + it 'sets the correct family/release for raspbian ' do + files = { + '/usr/bin/raspi-config' => 'data', + '/etc/debian_version' => '13.6', + } + platform = scan_with_files('linux', files) + + platform[:name].must_equal('raspbian') + platform[:family].must_equal('debian') + platform[:release].must_equal('13.6') + end + end + + describe 'everything else' do + it 'sets the correct family/release for debian ' do + platform = debian_scan('some_debian', '12.99') + + platform[:name].must_equal('debian') + platform[:family].must_equal('debian') + platform[:release].must_equal('12.99') + end + end + end + + describe '/etc/coreos/update.conf' do + it 'sets the correct family/release for coreos' do + lsb_release = "DISTRIB_ID=Container Linux by CoreOS\nDISTRIB_RELEASE=27.9" + files = { + '/etc/lsb-release' => lsb_release, + '/etc/coreos/update.conf' => 'data', + } + platform = scan_with_files('linux', files) + + platform[:name].must_equal('coreos') + platform[:family].must_equal('linux') + platform[:release].must_equal('27.9') + end + end + + describe '/etc/os-release' do + describe 'when not on a wrlinux build' do + it 'fail back to genaric linux' do + os_release = "ID_LIKE=cisco-unkwown\nVERSION=unknown" + files = { + '/etc/os-release' => os_release, + } + platform = scan_with_files('linux', files) + + platform[:name].must_equal('linux') + platform[:family].must_equal('linux') + end + end + + describe 'when on a wrlinux build' do + it 'sets the correct family/release for wrlinux' do + os_release = "ID_LIKE=cisco-wrlinux\nVERSION=cisco123" + files = { + '/etc/os-release' => os_release, + } + platform = scan_with_files('linux', files) + + platform[:name].must_equal('wrlinux') + platform[:family].must_equal('redhat') + platform[:release].must_equal('cisco123') + end + end + end +end diff --git a/test/unit/extras/os_common_test.rb b/test/unit/platforms/platform_test.rb similarity index 76% rename from test/unit/extras/os_common_test.rb rename to test/unit/platforms/platform_test.rb index a0e8ac7a..1184df16 100644 --- a/test/unit/extras/os_common_test.rb +++ b/test/unit/platforms/platform_test.rb @@ -1,30 +1,97 @@ # encoding: utf-8 -# author: Dominik Richter -# author: Christoph Hartmann require 'helper' -require 'train/extras' -describe 'os common plugin' do - let(:cls) { - Class.new(Train::Extras::OSCommon) do - def detect_family; end +describe 'platform' do + def mock_platform(x) + plat = Train::Platforms.name(x) + plat.family_hierarchy = mock_os_hierarchy(plat).flatten + plat.platform[:family] = plat.family_hierarchy[0] + plat.add_platform_methods + plat + end + + def mock_platform_family(x) + Train::Platforms.list[x] = nil if x == 'mock' + plat = Train::Platforms.name(x).in_family(x) + plat.family_hierarchy = mock_os_hierarchy(plat).flatten + plat.platform[:family] = plat.family_hierarchy[0] + plat.add_platform_methods + plat + end + + def mock_os_hierarchy(plat) + plat.families.each_with_object([]) do |(k, _v), memo| + memo << k.name + memo << mock_os_hierarchy(k) unless k.families.empty? end - } + end - def mock_platform(x) - cls.new(nil, { family: x }) + it 'set platform title' do + plat = mock_platform_family('mock') + plat.title.must_equal('Mock') + plat.title('The Best Mock') + plat.title.must_equal('The Best Mock') + end + + it 'set name and name override' do + plat = mock_platform_family('mock') + plat.name.must_equal('mock') + plat[:name].must_equal('mock') + plat.platform[:name] = 'mock2020' + plat.name.must_equal('mock2020') + plat[:name].must_equal('mock2020') + end + + it 'check families' do + plat = mock_platform_family('mock') + plat.families.keys[0].name.must_equal('mock') + end + + it 'check families with condition' do + Train::Platforms.list['mock'] = nil + plat = Train::Platforms.name('mock', arch: '= x86_64').in_family('linux') + plat.families.keys[0].name.must_equal('linux') + plat.families.values[0].must_equal({ arch: '= x86_64' }) + end + + it 'return direct families' do + plat = mock_platform_family('mock') + plat.in_family('mock2') + plat.in_family('mock3') + plat.direct_families.must_equal(["mock", "mock2", "mock3"]) + end + + it 'return to_hash' do + plat = mock_platform_family('mock') + plat.to_hash.must_equal({ family: "mock" }) + end + + it 'return name?' do + plat = Train::Platforms.name('windows_rc1') + defined?(plat.windows_rc1?).must_be_nil + plat.add_platform_methods + plat.windows_rc1?.must_equal(true) + end + + it 'add platform methods' do + Train::Platforms.list['mock'] = nil + plat = Train::Platforms.name('mock').in_family('linux') + defined?(plat.linux?).must_be_nil + plat.family_hierarchy = mock_os_hierarchy(plat).flatten + plat.add_platform_methods + plat.linux?.must_equal(true) end it 'provides a method to access platform data' do - family = rand - os = mock_platform(family) + family = 'test-os' + os = mock_platform_family(family) os[:family].must_equal family end it 'provides an accessor for the full hash' do - x = rand.to_s - os = mock_platform(x) + x = 'test-os' + os = mock_platform_family(x) os.to_hash.must_equal({ family: x }) end @@ -68,7 +135,7 @@ def mock_platform(x) describe 'with platform set to amazon' do let(:os) { mock_platform('amazon') } it { os.fedora?.must_equal(false) } - it { os.redhat?.must_equal(false) } + it { os.redhat?.must_equal(true) } it { os.debian?.must_equal(false) } it { os.suse?.must_equal(false) } it { os.linux?.must_equal(true) } @@ -278,10 +345,10 @@ def mock_platform(x) end describe 'with platform set to esx' do - let(:os) { mock_platform('esx') } + let(:os) { mock_platform('vmkernel') } it { os.solaris?.must_equal(false) } it { os.linux?.must_equal(false) } - it {os[:family].must_equal('esx')} + it { os[:family].must_equal('esx') } it { os.unix?.must_equal(false) } it { os.esx?.must_equal(true) } end @@ -290,10 +357,9 @@ def mock_platform(x) let(:os) { mock_platform('darwin') } it { os.solaris?.must_equal(false) } it { os.linux?.must_equal(false) } - it {os[:family].must_equal('darwin')} + it { os[:family].must_equal('bsd') } it { os.unix?.must_equal(true) } it { os.bsd?.must_equal(true) } it { os.esx?.must_equal(false) } end - end diff --git a/test/unit/platforms/platforms_test.rb b/test/unit/platforms/platforms_test.rb new file mode 100644 index 00000000..2712101c --- /dev/null +++ b/test/unit/platforms/platforms_test.rb @@ -0,0 +1,42 @@ +# encoding: utf-8 + +require 'helper' + +describe 'platforms' do + + it 'create platform' do + Train::Platforms.list['mock'] = nil + plat = Train::Platforms.name('mock') + Train::Platforms.name('mock').in_family('test') + Train::Platforms.name('mock').detect { true } + plat.title.must_equal('Mock') + plat.detect.call.must_equal(true) + plat.families.keys[0].name.must_equal('test') + end + + it 'create family' do + Train::Platforms.families['mock'] = nil + fam = Train::Platforms.family('mock') + Train::Platforms.family('mock').in_family('test') + Train::Platforms.family('mock').detect { true } + fam.title.must_equal('Mock Family') + fam.detect.call.must_equal(true) + fam.families.keys[0].name.must_equal('test') + end + + it 'return top platforms empty' do + Train::Platforms.stubs(:list).returns({}) + Train::Platforms.stubs(:families).returns({}) + top = Train::Platforms.top_platforms + top.count.must_equal(0) + end + + it 'return top platforms with data' do + plat = Train::Platforms.name('linux') + plat.stubs(:families).returns({}) + Train::Platforms.stubs(:list).returns({ 'linux' => plat }) + Train::Platforms.stubs(:families).returns({}) + top = Train::Platforms.top_platforms + top.count.must_equal(1) + end +end diff --git a/test/unit/transports/local_file_test.rb b/test/unit/transports/local_file_test.rb index c0cf3749..527d8fcd 100644 --- a/test/unit/transports/local_file_test.rb +++ b/test/unit/transports/local_file_test.rb @@ -6,8 +6,12 @@ require 'helper' require 'train/transports/local' +$transport = nil describe 'local file transport' do - let(:transport) { Train::Transports::Local.new } + let(:transport) do + $transport = Train::Transports::Local.new if $transport.nil? + $transport + end let(:connection) { transport.connection } it 'gets file contents' do diff --git a/test/unit/transports/local_test.rb b/test/unit/transports/local_test.rb index 2e7f9c1d..abceb97f 100644 --- a/test/unit/transports/local_test.rb +++ b/test/unit/transports/local_test.rb @@ -1,27 +1,20 @@ # encoding: utf-8 -# -# author: Dominik Richter -# author: Christoph Hartmann require 'helper' require 'train/transports/local' -# overwrite os detection to simplify mock tests, otherwise run_command tries to -# determine the OS first and fails the tests -class Train::Transports::Local::Connection - class OS < OSCommon - def initialize(backend) - super(backend, { family: 'train_mock_os' }) +$transport = nil +describe 'local transport' do + let(:transport) do + if $transport.nil? + plat = Train::Platforms.name('mock').in_family('linux') + plat.add_platform_methods + Train::Platforms::Detect.stubs(:scan).returns(plat) + $transport = Train::Transports::Local.new end - def detect_family - # no op, we do not need to detect the os - end + $transport end -end - -describe 'local transport' do - let(:transport) { Train::Transports::Local.new } let(:connection) { transport.connection } it 'can be instantiated' do diff --git a/test/unit/transports/mock_test.rb b/test/unit/transports/mock_test.rb index 3895660c..1b1032dc 100644 --- a/test/unit/transports/mock_test.rb +++ b/test/unit/transports/mock_test.rb @@ -72,14 +72,14 @@ describe 'when accessing a mocked os' do it 'has the default mock os faily set to unknown' do - connection.os[:family].must_equal 'unknown' + connection.os[:name].must_equal 'unknown' end it 'sets the OS to the mocked value' do - connection.mock_os({ family: 'centos' }) + connection.mock_os({ name: 'centos' }) connection.os.linux?.must_equal true connection.os.redhat?.must_equal true - connection.os[:family].must_equal 'centos' + connection.os[:family].must_equal 'redhat' end end diff --git a/test/unit/transports/ssh_test.rb b/test/unit/transports/ssh_test.rb index 513b0751..53e3cb3e 100644 --- a/test/unit/transports/ssh_test.rb +++ b/test/unit/transports/ssh_test.rb @@ -1,23 +1,15 @@ # encoding: utf-8 + require 'helper' require 'train/transports/ssh' -# overwrite os detection to simplify mock tests, otherwise run_command tries to -# determine the OS first and fails the tests -class Train::Transports::SSH::Connection - class OS < OSCommon - def initialize(backend) - super(backend, { family: 'train_mock_os' }) - end - - def detect_family - # no op, we do not need to detect the os - end - end -end - describe 'ssh transport' do - let(:cls) { Train::Transports::SSH } + let(:cls) { + plat = Train::Platforms.name('mock').in_family('linux') + plat.add_platform_methods + Train::Platforms::Detect.stubs(:scan).returns(plat) + Train::Transports::SSH + } let(:conf) {{ host: rand.to_s, password: rand.to_s, From efdab1d670374b89f372027eb29150dd875fd84c Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Thu, 26 Oct 2017 12:29:28 -0400 Subject: [PATCH 10/22] fix lint issues Signed-off-by: Jared Quick --- Gemfile | 1 - lib/train.rb | 1 - lib/train/exceptions.rb | 5 ---- lib/train/platforms.rb | 3 ++- lib/train/platforms/common.rb | 5 +--- lib/train/platforms/detect/scanner.rb | 10 ++++---- lib/train/platforms/specifications/os.rb | 30 +++++++++++++---------- lib/train/transports/local.rb | 1 - lib/train/transports/mock.rb | 1 - test/unit/platforms/detect/common_test.rb | 1 - 10 files changed, 25 insertions(+), 33 deletions(-) delete mode 100644 lib/train/exceptions.rb diff --git a/Gemfile b/Gemfile index a1123d67..ed43bede 100644 --- a/Gemfile +++ b/Gemfile @@ -20,7 +20,6 @@ group :test do gem 'rubocop', '~> 0.36.0' gem 'simplecov', '~> 0.10' gem 'concurrent-ruby', '~> 0.9' - gem 'pry-byebug' end group :integration do diff --git a/lib/train.rb b/lib/train.rb index 52157761..529138ae 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -7,7 +7,6 @@ require 'train/plugins' require 'train/errors' require 'train/platforms' -require 'train/exceptions' require 'uri' module Train diff --git a/lib/train/exceptions.rb b/lib/train/exceptions.rb deleted file mode 100644 index 72ecb480..00000000 --- a/lib/train/exceptions.rb +++ /dev/null @@ -1,5 +0,0 @@ -# encoding: utf-8 - -module Train::Exceptions - class PlatformDetectionFailed < StandardError; end -end diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index e7608fb0..1fbfe004 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -65,7 +65,8 @@ def self.list_all top_platforms = self.top_platforms top_platforms.each_value do |platform| puts platform.title - print_children(platform) if defined?(platform.children) end + print_children(platform) if defined?(platform.children) + end end def self.print_children(parent, pad = 2) diff --git a/lib/train/platforms/common.rb b/lib/train/platforms/common.rb index dc0fc06f..15e18a53 100644 --- a/lib/train/platforms/common.rb +++ b/lib/train/platforms/common.rb @@ -1,15 +1,12 @@ # encoding: utf-8 -# -# Author:: Jared Quick module Train::Platforms module Common - # Add a family connection. This will create a family # if it does not exist and add a child relationship. def in_family(family) if self.class == Train::Platforms::Family && @name == family - raise "Sorry you can not add a family inside itself '#{@name}.in_family(#{family})'" + fail "Sorry you can not add a family inside itself '#{@name}.in_family(#{family})'" end # add family to the family list diff --git a/lib/train/platforms/detect/scanner.rb b/lib/train/platforms/detect/scanner.rb index 8c9c5b0c..ebf508c2 100644 --- a/lib/train/platforms/detect/scanner.rb +++ b/lib/train/platforms/detect/scanner.rb @@ -24,7 +24,7 @@ def initialize(backend) def scan # start with the platform/families who have no families (the top levels) top = Train::Platforms.top_platforms - top.each do |name, plat| + top.each do |_name, plat| next unless plat.detect result = instance_eval(&plat.detect) next unless result == true @@ -38,7 +38,7 @@ def scan return get_platform(plat_result) end - raise Train::PlatformDetectionFailed, 'Sorry we did not find your platform' + fail Train::PlatformDetectionFailed, 'Sorry, we are unable to detect your platform' end def scan_children(parent) @@ -68,9 +68,9 @@ def scan_family_children(plat) def check_condition(condition) condition.each do |k, v| - opp, expected = v.strip.split(' ') - opp = '==' if opp == '=' - return false if @platform[k].nil? || !instance_eval("'#{@platform[k]}' #{opp} '#{expected}'") + op, expected = v.strip.split(' ') + op = '==' if op == '=' + return false if @platform[k].nil? || !instance_eval("'#{@platform[k]}' #{op} '#{expected}'") end true diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/specifications/os.rb index 3554e23d..d0f42d40 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -1,3 +1,7 @@ +# encoding: utf-8 + +# rubocop:disable Style/Next + plat = Train::Platforms plat.family('windows') @@ -99,7 +103,7 @@ true end } -# keep redhat after centos as a catchall for redhat base +# keep redhat after centos as a fallback for redhat base plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') .detect { lsb = read_linux_lsb @@ -149,11 +153,9 @@ } plat.name('wrlinux').title('Wind River Linux').in_family('redhat') .detect { - unless linux_os_release.nil? - if linux_os_release['ID_LIKE'] =~ /wrlinux/i - @platform[:release] = linux_os_release['VERSION'] - true - end + if linux_os_release && linux_os_release['ID_LIKE'] =~ /wrlinux/i + @platform[:release] = linux_os_release['VERSION'] + true end } plat.name('amazon').title('Amazon Linux').in_family('redhat') @@ -173,7 +175,6 @@ plat.family('suse').in_family('linux') .detect { if !(suse = unix_file_contents('/etc/SuSE-release')).nil? - puts suse.inspect version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' @platform[:release] = version @@ -241,7 +242,7 @@ # coreos plat.name('coreos').title('CoreOS Linux').in_family('linux') .detect { - if !unix_file_contents('/etc/coreos/update.conf').nil? + unless unix_file_contents('/etc/coreos/update.conf').nil? lsb = read_linux_lsb @platform[:release] = lsb[:release] true @@ -275,6 +276,7 @@ plat.family('arista_eos').title('Arista EOS Family').in_family('unix') .detect { # we need a better way to determin this family + # for now we are going to just try each platform true } plat.name('arista_eos').title('Arista EOS').in_family('arista_eos') @@ -390,10 +392,10 @@ true elsif /^\s*(Solaris)\s.*$/ =~ rel true - else - # must be some unknown solaris - true end + + # must be some unknown solaris + true } # hpux @@ -403,13 +405,15 @@ } plat.name('hpux').title('Hpux').in_family('hpux') .detect { - @platform[:release] = unix_uname_r.lines[0].chomp - true + @platform[:release] = unix_uname_r.lines[0].chomp + true } # bsd family plat.family('bsd').in_family('unix') .detect { + # we need a better way to determin this family + # for now we are going to just try each platform true } plat.name('darwin').title('Darwin').in_family('bsd') diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index 4577d9f5..f8f2268b 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -46,7 +46,6 @@ def platform @platform ||= Train::Platforms::Detect.scan(self) end - def file(path) @files[path] ||= File.new(self, path) end diff --git a/lib/train/transports/mock.rb b/lib/train/transports/mock.rb index 23928af4..04f99d5c 100644 --- a/lib/train/transports/mock.rb +++ b/lib/train/transports/mock.rb @@ -77,7 +77,6 @@ def mock_os(value) platform.platform[:family] = platform.family_hierarchy[0] platform.add_platform_methods @os = platform - @os end def mock_os_hierarchy(plat) diff --git a/test/unit/platforms/detect/common_test.rb b/test/unit/platforms/detect/common_test.rb index b3fb71ae..53bfd654 100644 --- a/test/unit/platforms/detect/common_test.rb +++ b/test/unit/platforms/detect/common_test.rb @@ -38,7 +38,6 @@ def initialize detector.instance_variable_set(:@backend, be) detector.instance_variable_set(:@files, {}) detector.unix_file_contents('/etc/fstab').must_equal('test') - # puts detector.instance_variable_get(:@files).inspect end it 'return new file contents cached' do From 6c273363ed8288d33429e7a691ee6686dc72938c Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Thu, 26 Oct 2017 17:54:03 -0400 Subject: [PATCH 11/22] add unknown fallback incase a platform option is not detected Signed-off-by: Jared Quick --- lib/train/platform.rb | 6 +++++- test/unit/platforms/platform_test.rb | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/train/platform.rb b/lib/train/platform.rb index 165157c0..c9e233a5 100644 --- a/lib/train/platform.rb +++ b/lib/train/platform.rb @@ -31,7 +31,11 @@ def name # This is for backwords compatability with # the current inspec os resource. def[](name) - send(name) + if respond_to?(name) + send(name) + else + 'unknown' + end end def title(title = nil) diff --git a/test/unit/platforms/platform_test.rb b/test/unit/platforms/platform_test.rb index 1184df16..8025b1df 100644 --- a/test/unit/platforms/platform_test.rb +++ b/test/unit/platforms/platform_test.rb @@ -67,6 +67,11 @@ def mock_os_hierarchy(plat) plat.to_hash.must_equal({ family: "mock" }) end + it 'return unknown release' do + plat = mock_platform_family('mock') + plat[:release].must_equal('unknown') + end + it 'return name?' do plat = Train::Platforms.name('windows_rc1') defined?(plat.windows_rc1?).must_be_nil From aff0d80f4e4f9f99ce8929ff5dfa5456e4c98070 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Thu, 26 Oct 2017 18:22:23 -0400 Subject: [PATCH 12/22] Merge branch 'master' into jq/platform_objects Signed-off-by: Jared Quick --- lib/train/extras.rb | 1 + lib/train/extras/file_qnx.rb | 34 ++++++++++++++++++++++++++ lib/train/transports/ssh_connection.rb | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 lib/train/extras/file_qnx.rb diff --git a/lib/train/extras.rb b/lib/train/extras.rb index 697dd887..2f9d37d1 100644 --- a/lib/train/extras.rb +++ b/lib/train/extras.rb @@ -7,6 +7,7 @@ module Train::Extras require 'train/extras/file_common' require 'train/extras/file_unix' require 'train/extras/file_aix' + require 'train/extras/file_qnx' require 'train/extras/file_linux' require 'train/extras/file_windows' require 'train/extras/stat' diff --git a/lib/train/extras/file_qnx.rb b/lib/train/extras/file_qnx.rb new file mode 100644 index 00000000..0b61ee53 --- /dev/null +++ b/lib/train/extras/file_qnx.rb @@ -0,0 +1,34 @@ +# encoding: utf-8 +# author: Christoph Hartmann +# author: Dominik Richter + +module Train::Extras + class QnxFile < UnixFile + def content + cat = 'cat' + cat = '/proc/boot/cat' if @backend.os[:release].to_i >= 7 + @content ||= case + when !exist? + nil + else + @backend.run_command("#{cat} #{@spath}").stdout || '' + end + end + + def type + if @backend.run_command("file #{@spath}").stdout.include?('directory') + return :directory + else + return :file + end + end + + %w{ + mode owner group uid gid mtime size selinux_label link_path mounted stat + }.each do |field| + define_method field.to_sym do + fail NotImplementedError, "QNX does not implement the #{m}() method yet." + end + end + end +end diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index 5e005675..0c9abeba 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -69,6 +69,8 @@ def file(path) AixFile.new(self, path) elsif os.solaris? UnixFile.new(self, path) + elsif os[:name] == 'qnx' + QnxFile.new(self, path) else LinuxFile.new(self, path) end From 7fd444d3011449c089925ba9da0b8ff51d935e6f Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Mon, 30 Oct 2017 10:23:25 -0400 Subject: [PATCH 13/22] add skip empty line on os_release detect Signed-off-by: Jared Quick --- lib/train/platforms/detect/os_linux.rb | 1 + lib/train/transports/local.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb index 467e91ac..dec83188 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/os_linux.rb @@ -30,6 +30,7 @@ def parse_os_release_info(raw) raw.lines.each_with_object({}) do |line, memo| line.strip! + next if line.empty? key, value = line.split('=', 2) memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty? end diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index d5df1362..271440c4 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -17,7 +17,6 @@ def connection(_ = nil) end class Connection < BaseConnection - def initialize(options) super(options) @cmd_wrapper = nil From 021e6fd4d904e94174666de9f9a85dc7b8576fa8 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Mon, 30 Oct 2017 10:45:48 -0400 Subject: [PATCH 14/22] removed unneeded platform type Signed-off-by: Jared Quick --- lib/train/platforms/detect/os_windows.rb | 1 - lib/train/platforms/specifications/os.rb | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/train/platforms/detect/os_windows.rb b/lib/train/platforms/detect/os_windows.rb index 095c977a..153d3014 100644 --- a/lib/train/platforms/detect/os_windows.rb +++ b/lib/train/platforms/detect/os_windows.rb @@ -23,7 +23,6 @@ def detect_windows # try to use wmic, but lets keep it optional read_wmic - @platform[:type] = 'windows' true end diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/specifications/os.rb index d0f42d40..9e231aeb 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -7,7 +7,6 @@ plat.family('windows') .detect { if winrm? || (@backend.local? && rbconfig(/mswin|mingw32|windows/)) - @platform[:type] = 'windows' true end } From 1c503407990c139ff9362a3f0e9d9e3599ff6597 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Tue, 31 Oct 2017 16:12:31 -0400 Subject: [PATCH 15/22] fix style issues and clear up logic Signed-off-by: Jared Quick --- lib/train/platform.rb | 18 ++++++------ lib/train/platforms.rb | 2 +- lib/train/platforms/detect/os_common.rb | 29 ++++++++++--------- lib/train/platforms/detect/scanner.rb | 8 ++--- lib/train/platforms/family.rb | 4 +-- lib/train/transports/docker.rb | 7 ++--- lib/train/transports/local.rb | 7 ++--- lib/train/transports/ssh_connection.rb | 7 ++--- lib/train/transports/winrm_connection.rb | 7 ++--- .../{common_test.rb => os_common_test.rb} | 2 +- .../{linux_test.rb => os_linux_test.rb} | 0 .../{windows_test.rb => os_windows_test.rb} | 0 12 files changed, 43 insertions(+), 48 deletions(-) rename test/unit/platforms/detect/{common_test.rb => os_common_test.rb} (98%) rename test/unit/platforms/detect/{linux_test.rb => os_linux_test.rb} (100%) rename test/unit/platforms/detect/{windows_test.rb => os_windows_test.rb} (100%) diff --git a/lib/train/platform.rb b/lib/train/platform.rb index c9e233a5..62425908 100644 --- a/lib/train/platform.rb +++ b/lib/train/platform.rb @@ -3,7 +3,7 @@ module Train class Platform include Train::Platforms::Common - attr_accessor :condition, :families, :backend, :platform, :family_hierarchy + attr_accessor :backend, :condition, :families, :family_hierarchy, :platform def initialize(name, condition = {}) @name = name @@ -12,7 +12,7 @@ def initialize(name, condition = {}) @family_hierarchy = [] @platform = {} @detect = nil - @title = name =~ /^[[:alpha:]]+$/ ? name.capitalize : name + @title = name.to_s.capitalize # add itself to the platform list Train::Platforms.list[name] = self @@ -48,7 +48,7 @@ def to_hash @platform end - # Add genaric family? and platform methods to an existing platform + # Add generic family? and platform methods to an existing platform # # This is done later to add any custom # families/properties that were created @@ -56,25 +56,25 @@ def add_platform_methods family_list = Train::Platforms.families family_list.each_value do |k| next if respond_to?(k.name + '?') - define_singleton_method(k.name + '?') { + define_singleton_method(k.name + '?') do family_hierarchy.include?(k.name) - } + end end # Helper methods for direct platform info @platform.each_key do |m| next if respond_to?(m) - define_singleton_method(m) { + define_singleton_method(m) do @platform[m] - } + end end # Create method for name if its not already true plat_name = name.downcase.tr(' ', '_') + '?' return if respond_to?(plat_name) - define_singleton_method(plat_name) { + define_singleton_method(plat_name) do true - } + end end end end diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 1fbfe004..4fc7f797 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -73,7 +73,7 @@ def self.print_children(parent, pad = 2) parent.children.each do |key, value| obj = key puts "#{' ' * pad}-> #{obj.title}#{value unless value.empty?}" - print_children(obj, pad + 2) unless !defined?(obj.children) || obj.children.nil? + print_children(obj, pad + 2) if defined?(obj.children) && !obj.children.nil? end end end diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index b403cdcf..78936aec 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -20,36 +20,37 @@ def winrm? def unix_file_contents(path) # keep a log of files incase multiple checks call the same one - return @files[path] unless @files[path].nil? + return @files[path] if @files.key?(path) res = @backend.run_command("test -f #{path} && cat #{path}") - @files[path] = res.stdout # ignore files that can't be read - return nil if res.exit_status != 0 - res.stdout + @files[path] = res.exit_status.zero? ? res.stdout : nil + @files[path] end def unix_file_exist?(path) @backend.run_command("test -f #{path}").exit_status.zero? end + def uname_call(cmd) + res = @backend.run_command(cmd).stdout + res.strip! unless res.nil? + res + end + def unix_uname_s - return @uname[:s] unless @uname[:s].nil? - @uname[:s] = @backend.run_command('uname -s').stdout + return @uname[:s] if @uname.key?(:s) + @uname[:s] = uname_call('uname -s') end def unix_uname_r - return @uname[:r] unless @uname[:r].nil? - @uname[:r] = begin - res = @backend.run_command('uname -r').stdout - res.strip! unless res.nil? - res - end + return @uname[:r] if @uname.key?(:r) + @uname[:r] = uname_call('uname -r') end def unix_uname_m - return @uname[:m] unless @uname[:m].nil? - @uname[:m] = @backend.run_command('uname -m').stdout.chomp + return @uname[:m] if @uname.key?(:m) + @uname[:m] = uname_call('uname -m') end end end diff --git a/lib/train/platforms/detect/scanner.rb b/lib/train/platforms/detect/scanner.rb index ebf508c2..92f68a76 100644 --- a/lib/train/platforms/detect/scanner.rb +++ b/lib/train/platforms/detect/scanner.rb @@ -26,8 +26,7 @@ def scan top = Train::Platforms.top_platforms top.each do |_name, plat| next unless plat.detect - result = instance_eval(&plat.detect) - next unless result == true + next unless instance_eval(&plat.detect) == true # if we have a match start looking at the children plat_result = scan_children(plat) @@ -44,8 +43,7 @@ def scan def scan_children(parent) parent.children.each do |plat, condition| next if plat.detect.nil? - result = instance_eval(&plat.detect) - next unless result == true + next unless instance_eval(&plat.detect) == true if plat.class == Train::Platform @platform[:family] = parent.name @@ -60,7 +58,7 @@ def scan_children(parent) end def scan_family_children(plat) - child_result = scan_children(plat) if !plat.children.nil? + child_result = scan_children(plat) unless plat.children.nil? return if child_result.nil? @family_hierarchy << plat.name child_result diff --git a/lib/train/platforms/family.rb b/lib/train/platforms/family.rb index dc438de5..61eb0d04 100644 --- a/lib/train/platforms/family.rb +++ b/lib/train/platforms/family.rb @@ -3,7 +3,7 @@ module Train::Platforms class Family include Train::Platforms::Common - attr_accessor :name, :condition, :families, :children + attr_accessor :children, :condition, :families, :name def initialize(name, condition) @name = name @@ -11,7 +11,7 @@ def initialize(name, condition) @families = {} @children = {} @detect = nil - @title = name =~ /^[[:alpha:]]+$/ ? "#{name.capitalize} Family" : name + @title = "#{name.to_s.capitalize} Family" # add itself to the families list Train::Platforms.families[@name.to_s] = self diff --git a/lib/train/transports/docker.rb b/lib/train/transports/docker.rb index 57f78002..c63dfd7f 100644 --- a/lib/train/transports/docker.rb +++ b/lib/train/transports/docker.rb @@ -69,14 +69,13 @@ def close # nothing to do at the moment end - def os - platform - end - def platform @platform ||= Train::Platforms::Detect.scan(self) end + # we need to keep os as a method for backwards compatibility with inspec + alias os platform + def file(path) @files[path] ||=\ if os.aix? diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index 271440c4..9d550322 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -36,14 +36,13 @@ def local? true end - def os - platform - end - def platform @platform ||= Train::Platforms::Detect.scan(self) end + # we need to keep os as a method for backwards compatibility with inspec + alias os platform + def file(path) @files[path] ||= \ if os.windows? diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index 6a06a235..c0f070c0 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -55,14 +55,13 @@ def close @session = nil end - def os - platform - end - def platform @platform ||= Train::Platforms::Detect.scan(self) end + # we need to keep os as a method for backwards compatibility with inspec + alias os platform + def file(path) @files[path] ||= \ if os.aix? diff --git a/lib/train/transports/winrm_connection.rb b/lib/train/transports/winrm_connection.rb index 567cf9f8..4f1f704e 100644 --- a/lib/train/transports/winrm_connection.rb +++ b/lib/train/transports/winrm_connection.rb @@ -46,14 +46,13 @@ def close @session = nil end - def os - platform - end - def platform @platform ||= Train::Platforms::Detect.scan(self) end + # we need to keep os as a method for backwards compatibility with inspec + alias os platform + def file(path) @files[path] ||= Train::File::Remote::Windows.new(self, path) end diff --git a/test/unit/platforms/detect/common_test.rb b/test/unit/platforms/detect/os_common_test.rb similarity index 98% rename from test/unit/platforms/detect/common_test.rb rename to test/unit/platforms/detect/os_common_test.rb index 53bfd654..5566e18f 100644 --- a/test/unit/platforms/detect/common_test.rb +++ b/test/unit/platforms/detect/os_common_test.rb @@ -33,7 +33,7 @@ def initialize it 'return new file contents' do be = mock('Backend') output = mock('Output', exit_status: 0) - output.expects(:stdout).twice.returns('test') + output.expects(:stdout).returns('test') be.stubs(:run_command).with('test -f /etc/fstab && cat /etc/fstab').returns(output) detector.instance_variable_set(:@backend, be) detector.instance_variable_set(:@files, {}) diff --git a/test/unit/platforms/detect/linux_test.rb b/test/unit/platforms/detect/os_linux_test.rb similarity index 100% rename from test/unit/platforms/detect/linux_test.rb rename to test/unit/platforms/detect/os_linux_test.rb diff --git a/test/unit/platforms/detect/windows_test.rb b/test/unit/platforms/detect/os_windows_test.rb similarity index 100% rename from test/unit/platforms/detect/windows_test.rb rename to test/unit/platforms/detect/os_windows_test.rb From 7cc026725351165531e388c978a0ba55e5e8f477 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Tue, 31 Oct 2017 17:31:11 -0400 Subject: [PATCH 16/22] style fixes and wrapped os specifications in a class Signed-off-by: Jared Quick --- lib/train.rb | 3 - lib/train/platforms.rb | 1 + lib/train/platforms/common.rb | 2 +- lib/train/platforms/detect.rb | 1 + lib/train/platforms/detect/os_common.rb | 10 +- lib/train/platforms/detect/os_linux.rb | 31 +- lib/train/platforms/detect/scanner.rb | 1 - lib/train/platforms/specifications/os.rb | 863 ++++++++++++----------- lib/train/transports/docker.rb | 1 + lib/train/transports/local.rb | 1 + lib/train/transports/mock.rb | 1 + lib/train/transports/ssh_connection.rb | 1 + lib/train/transports/winrm_connection.rb | 1 + 13 files changed, 471 insertions(+), 446 deletions(-) diff --git a/lib/train.rb b/lib/train.rb index 529138ae..c85e90cf 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -17,9 +17,6 @@ module Train # @param [Array] *args list of arguments for the plugin # @return [Transport] instance of the new transport or nil def self.create(name, *args) - # require built in detect platforms at this level so any manual ones will be at the top - require 'train/platforms/detect' - cls = load_transport(name) cls.new(*args) unless cls.nil? end diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 4fc7f797..513179b5 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -3,6 +3,7 @@ require 'train/platforms/common' require 'train/platforms/family' require 'train/platform' +require 'train/platforms/detect' module Train::Platforms class << self diff --git a/lib/train/platforms/common.rb b/lib/train/platforms/common.rb index 15e18a53..2cf2ba53 100644 --- a/lib/train/platforms/common.rb +++ b/lib/train/platforms/common.rb @@ -6,7 +6,7 @@ module Common # if it does not exist and add a child relationship. def in_family(family) if self.class == Train::Platforms::Family && @name == family - fail "Sorry you can not add a family inside itself '#{@name}.in_family(#{family})'" + fail "Unable to add family #{@name} to itself: '#{@name}.in_family(#{family})'" end # add family to the family list diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index 02aada92..f7cdd31a 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -1,5 +1,6 @@ # encoding: utf-8 +require 'train/platforms/specifications/os' require 'train/platforms/detect/scanner' module Train::Platforms diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/os_common.rb index 78936aec..c9d38667 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/os_common.rb @@ -9,7 +9,7 @@ module OSCommon include Train::Platforms::Detect::Linux include Train::Platforms::Detect::Windows - def rbconfig(regex) + def ruby_host_os(regex) ::RbConfig::CONFIG['host_os'] =~ regex end @@ -32,7 +32,7 @@ def unix_file_exist?(path) @backend.run_command("test -f #{path}").exit_status.zero? end - def uname_call(cmd) + def command_output(cmd) res = @backend.run_command(cmd).stdout res.strip! unless res.nil? res @@ -40,17 +40,17 @@ def uname_call(cmd) def unix_uname_s return @uname[:s] if @uname.key?(:s) - @uname[:s] = uname_call('uname -s') + @uname[:s] = command_output('uname -s') end def unix_uname_r return @uname[:r] if @uname.key?(:r) - @uname[:r] = uname_call('uname -r') + @uname[:r] = command_output('uname -r') end def unix_uname_m return @uname[:m] if @uname.key?(:m) - @uname[:m] = uname_call('uname -m') + @uname[:m] = command_output('uname -m') end end end diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/os_linux.rb index dec83188..89246dac 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/os_linux.rb @@ -3,13 +3,18 @@ module Train::Platforms::Detect module Linux def redhatish_platform(conf) - conf[/^red hat/i] ? 'redhat' : conf[/(\w+)/i, 1].downcase + conf =~ /^red hat/i ? 'redhat' : /(\w+)/i.match(conf)[1].downcase end def redhatish_version(conf) - return conf[/((\d+) \(Rawhide\))/i, 1].downcase if conf[/rawhide/i] - return conf[/Linux ((\d+|\.)+)/i, 1] if conf[/derived from .*linux/i] - conf[/release ([\d\.]+)/, 1] + case conf + when /rawhide/i + /((\d+) \(Rawhide\))/i.match(conf)[1].downcase + when /derived from .*linux/i + /Linux ((\d+|\.)+)/i.match(conf)[1] + else + /release ([\d\.]+)/.match(conf)[1] + end end def linux_os_release @@ -37,18 +42,24 @@ def parse_os_release_info(raw) end def lsb_config(content) + id = /^DISTRIB_ID=["']?(.+?)["']?$/.match(content) + release = /^DISTRIB_RELEASE=["']?(.+?)["']?$/.match(content) + codename = /^DISTRIB_CODENAME=["']?(.+?)["']?$/.match(content) { - id: content[/^DISTRIB_ID=["']?(.+?)["']?$/, 1], - release: content[/^DISTRIB_RELEASE=["']?(.+?)["']?$/, 1], - codename: content[/^DISTRIB_CODENAME=["']?(.+?)["']?$/, 1], + id: id.nil? ? nil : id[1], + release: release.nil? ? nil : release[1], + codename: codename.nil? ? nil : codename[1], } end def lsb_release(content) + id = /^Distributor ID:\s+(.+)$/.match(content) + release = /^Release:\s+(.+)$/.match(content) + codename = /^Codename:\s+(.+)$/.match(content) { - id: content[/^Distributor ID:\s+(.+)$/, 1], - release: content[/^Release:\s+(.+)$/, 1], - codename: content[/^Codename:\s+(.+)$/, 1], + id: id.nil? ? nil : id[1], + release: release.nil? ? nil : release[1], + codename: codename.nil? ? nil : codename[1], } end diff --git a/lib/train/platforms/detect/scanner.rb b/lib/train/platforms/detect/scanner.rb index 92f68a76..081ad33a 100644 --- a/lib/train/platforms/detect/scanner.rb +++ b/lib/train/platforms/detect/scanner.rb @@ -1,6 +1,5 @@ # encoding: utf-8 -require 'train/platforms/specifications/os' require 'train/platforms/detect/os_common' module Train::Platforms::Detect diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/specifications/os.rb index 9e231aeb..220855b8 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/specifications/os.rb @@ -1,456 +1,467 @@ # encoding: utf-8 # rubocop:disable Style/Next +# rubocop:disable Metrics/AbcSize +# rubocop:disable Metrics/CyclomaticComplexity +# rubocop:disable Metrics/ClassLength +# rubocop:disable Metrics/MethodLength +# rubocop:disable Metrics/PerceivedComplexity -plat = Train::Platforms +module Train::Platforms::Specifications + class OS + def self.load_specifications + plat = Train::Platforms -plat.family('windows') - .detect { - if winrm? || (@backend.local? && rbconfig(/mswin|mingw32|windows/)) - true - end - } -# windows platform -plat.name('windows').in_family('windows') - .detect { - true if detect_windows == true - } + plat.family('windows') + .detect { + if winrm? || (@backend.local? && ruby_host_os(/mswin|mingw32|windows/)) + true + end + } + # windows platform + plat.name('windows').in_family('windows') + .detect { + true if detect_windows == true + } -# unix master family -plat.family('unix') - .detect { - if unix_uname_s =~ /./ - @platform[:arch] = unix_uname_m - true - end - } + # unix master family + plat.family('unix') + .detect { + if unix_uname_s =~ /./ + @platform[:arch] = unix_uname_m + true + end + } -# linux master family -plat.family('linux').in_family('unix') - .detect { - true if unix_uname_s =~ /linux/i - } + # linux master family + plat.family('linux').in_family('unix') + .detect { + true if unix_uname_s =~ /linux/i + } -# debian family -plat.family('debian').in_family('linux') - .detect { - true unless unix_file_contents('/etc/debian_version').nil? - } -plat.name('ubuntu').title('Ubuntu Linux').in_family('debian') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /ubuntu/i - @platform[:release] = lsb[:release] - true - end - } -plat.name('linuxmint').title('LinuxMint').in_family('debian') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /linuxmint/i - @platform[:release] = lsb[:release] - true - end - } -plat.name('raspbian').title('Raspbian Linux').in_family('debian') - .detect { - if unix_file_exist?('/usr/bin/raspi-config') - @platform[:release] = unix_file_contents('/etc/debian_version').chomp - true - end - } -plat.name('debian').title('Debian Linux').in_family('debian') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /debian/i - @platform[:release] = lsb[:release] - true - end + # debian family + plat.family('debian').in_family('linux') + .detect { + true unless unix_file_contents('/etc/debian_version').nil? + } + plat.name('ubuntu').title('Ubuntu Linux').in_family('debian') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /ubuntu/i + @platform[:release] = lsb[:release] + true + end + } + plat.name('linuxmint').title('LinuxMint').in_family('debian') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /linuxmint/i + @platform[:release] = lsb[:release] + true + end + } + plat.name('raspbian').title('Raspbian Linux').in_family('debian') + .detect { + if unix_file_exist?('/usr/bin/raspi-config') + @platform[:release] = unix_file_contents('/etc/debian_version').chomp + true + end + } + plat.name('debian').title('Debian Linux').in_family('debian') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /debian/i + @platform[:release] = lsb[:release] + true + end - # if we get this far we have to be some type of debian - true - } + # if we get this far we have to be some type of debian + true + } -# fedora family -plat.family('fedora').in_family('linux') - .detect { - true if linux_os_release && linux_os_release['NAME'] =~ /fedora/i - } -plat.name('fedora').title('Fedora').in_family('fedora') - .detect { - @platform[:release] = linux_os_release['VERSION_ID'] - true - } + # fedora family + plat.family('fedora').in_family('linux') + .detect { + true if linux_os_release && linux_os_release['NAME'] =~ /fedora/i + } + plat.name('fedora').title('Fedora').in_family('fedora') + .detect { + @platform[:release] = linux_os_release['VERSION_ID'] + true + } -# redhat family -plat.family('redhat').in_family('linux') - .detect { - # I am not sure this returns true for all redhats in this family - # for now we are going to just try each platform - # return true unless unix_file_contents('/etc/redhat-release').nil? + # redhat family + plat.family('redhat').in_family('linux') + .detect { + # I am not sure this returns true for all redhats in this family + # for now we are going to just try each platform + # return true unless unix_file_contents('/etc/redhat-release').nil? - true - } -plat.name('centos').title('Centos Linux').in_family('redhat') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /centos/i - @platform[:release] = lsb[:release] - true - elsif unix_file_contents('/etc/os-release') =~ /centos/i - @platform[:release] = redhatish_version(unix_file_contents('/etc/redhat-release')) - true - end - } -# keep redhat after centos as a fallback for redhat base -plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /redhat/i - @platform[:release] = lsb[:release] - true - elsif !(raw = unix_file_contents('/etc/redhat-release')).nil? - # must be some type of redhat - @platform[:name] = redhatish_platform(raw) - @platform[:release] = redhatish_version(raw) - true - end - } -plat.name('oracle').title('Oracle Linux').in_family('redhat') - .detect { - if !(raw = unix_file_contents('/etc/oracle-release')).nil? - @platform[:release] = redhatish_version(raw) - true - elsif !(raw = unix_file_contents('/etc/enterprise-release')).nil? - @platform[:release] = redhatish_version(raw) - true - end - } -plat.name('scientific').title('Scientific Linux').in_family('redhat') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /scientificsl/i - @platform[:release] = lsb[:release] - true - end - } -plat.name('xenserver').title('Xenserer Linux').in_family('redhat') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /xenserver/i - @platform[:release] = lsb[:release] - true - end - } -plat.name('parallels-release').title('Parallels Linux').in_family('redhat') - .detect { - if !(raw = unix_file_contents('/etc/parallels-release')).nil? - @platform[:name] = redhatish_platform(raw) - @platform[:release] = raw[/(\d\.\d\.\d)/, 1] - true - end - } -plat.name('wrlinux').title('Wind River Linux').in_family('redhat') - .detect { - if linux_os_release && linux_os_release['ID_LIKE'] =~ /wrlinux/i - @platform[:release] = linux_os_release['VERSION'] - true - end - } -plat.name('amazon').title('Amazon Linux').in_family('redhat') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /amazon/i - @platform[:release] = lsb[:release] - true - elsif !(raw = unix_file_contents('/etc/system-release')).nil? - @platform[:name] = redhatish_platform(raw) - @platform[:release] = redhatish_version(raw) - true - end - } + true + } + plat.name('centos').title('Centos Linux').in_family('redhat') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /centos/i + @platform[:release] = lsb[:release] + true + elsif unix_file_contents('/etc/os-release') =~ /centos/i + @platform[:release] = redhatish_version(unix_file_contents('/etc/redhat-release')) + true + end + } + # keep redhat after centos as a fallback for redhat base + plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /redhat/i + @platform[:release] = lsb[:release] + true + elsif !(raw = unix_file_contents('/etc/redhat-release')).nil? + # must be some type of redhat + @platform[:name] = redhatish_platform(raw) + @platform[:release] = redhatish_version(raw) + true + end + } + plat.name('oracle').title('Oracle Linux').in_family('redhat') + .detect { + if !(raw = unix_file_contents('/etc/oracle-release')).nil? + @platform[:release] = redhatish_version(raw) + true + elsif !(raw = unix_file_contents('/etc/enterprise-release')).nil? + @platform[:release] = redhatish_version(raw) + true + end + } + plat.name('scientific').title('Scientific Linux').in_family('redhat') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /scientificsl/i + @platform[:release] = lsb[:release] + true + end + } + plat.name('xenserver').title('Xenserer Linux').in_family('redhat') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /xenserver/i + @platform[:release] = lsb[:release] + true + end + } + plat.name('parallels-release').title('Parallels Linux').in_family('redhat') + .detect { + if !(raw = unix_file_contents('/etc/parallels-release')).nil? + @platform[:name] = redhatish_platform(raw) + @platform[:release] = raw[/(\d\.\d\.\d)/, 1] + true + end + } + plat.name('wrlinux').title('Wind River Linux').in_family('redhat') + .detect { + if linux_os_release && linux_os_release['ID_LIKE'] =~ /wrlinux/i + @platform[:release] = linux_os_release['VERSION'] + true + end + } + plat.name('amazon').title('Amazon Linux').in_family('redhat') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /amazon/i + @platform[:release] = lsb[:release] + true + elsif !(raw = unix_file_contents('/etc/system-release')).nil? + @platform[:name] = redhatish_platform(raw) + @platform[:release] = redhatish_version(raw) + true + end + } -# suse family -plat.family('suse').in_family('linux') - .detect { - if !(suse = unix_file_contents('/etc/SuSE-release')).nil? - version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') - version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' - @platform[:release] = version - true - end - } -plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') - .detect { - true if unix_file_contents('/etc/SuSE-release') =~ /^openSUSE/ - } -plat.name('suse').title('Suse Linux').in_family('suse') - .detect { - true if unix_file_contents('/etc/SuSE-release') =~ /suse/ - } + # suse family + plat.family('suse').in_family('linux') + .detect { + if !(suse = unix_file_contents('/etc/SuSE-release')).nil? + version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.') + version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == '' + @platform[:release] = version + true + end + } + plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') + .detect { + true if unix_file_contents('/etc/SuSE-release') =~ /^openSUSE/ + } + plat.name('suse').title('Suse Linux').in_family('suse') + .detect { + true if unix_file_contents('/etc/SuSE-release') =~ /suse/ + } -# arch -plat.name('arch').title('Arch Linux').in_family('linux') - .detect { - if !unix_file_contents('/etc/arch-release').nil? - # Because this is a rolling release distribution, - # use the kernel release, ex. 4.1.6-1-ARCH - @platform[:release] = unix_uname_r - true - end - } + # arch + plat.name('arch').title('Arch Linux').in_family('linux') + .detect { + if !unix_file_contents('/etc/arch-release').nil? + # Because this is a rolling release distribution, + # use the kernel release, ex. 4.1.6-1-ARCH + @platform[:release] = unix_uname_r + true + end + } -# slackware -plat.name('slackware').title('Slackware Linux').in_family('linux') - .detect { - if !(raw = unix_file_contents('/etc/slackware-version')).nil? - @platform[:release] = raw.scan(/(\d+|\.+)/).join - true - end - } + # slackware + plat.name('slackware').title('Slackware Linux').in_family('linux') + .detect { + if !(raw = unix_file_contents('/etc/slackware-version')).nil? + @platform[:release] = raw.scan(/(\d+|\.+)/).join + true + end + } -# gentoo -plat.name('gentoo').title('Gentoo Linux').in_family('linux') - .detect { - if !(raw = unix_file_contents('/etc/gentoo-release')).nil? - @platform[:release] = raw.scan(/(\d+|\.+)/).join - true - end - } + # gentoo + plat.name('gentoo').title('Gentoo Linux').in_family('linux') + .detect { + if !(raw = unix_file_contents('/etc/gentoo-release')).nil? + @platform[:release] = raw.scan(/(\d+|\.+)/).join + true + end + } -# exherbo -plat.name('exherbo').title('Exherbo Linux').in_family('linux') - .detect { - unless unix_file_contents('/etc/exherbo-release').nil? - # Because this is a rolling release distribution, - # use the kernel release, ex. 4.1.6 - @platform[:release] = unix_uname_r - true - end - } + # exherbo + plat.name('exherbo').title('Exherbo Linux').in_family('linux') + .detect { + unless unix_file_contents('/etc/exherbo-release').nil? + # Because this is a rolling release distribution, + # use the kernel release, ex. 4.1.6 + @platform[:release] = unix_uname_r + true + end + } -# alpine -plat.name('alpine').title('Alpine Linux').in_family('linux') - .detect { - if !(raw = unix_file_contents('/etc/alpine-release')).nil? - @platform[:release] = raw.strip - true - end - } + # alpine + plat.name('alpine').title('Alpine Linux').in_family('linux') + .detect { + if !(raw = unix_file_contents('/etc/alpine-release')).nil? + @platform[:release] = raw.strip + true + end + } -# coreos -plat.name('coreos').title('CoreOS Linux').in_family('linux') - .detect { - unless unix_file_contents('/etc/coreos/update.conf').nil? - lsb = read_linux_lsb - @platform[:release] = lsb[:release] - true - end - } + # coreos + plat.name('coreos').title('CoreOS Linux').in_family('linux') + .detect { + unless unix_file_contents('/etc/coreos/update.conf').nil? + lsb = read_linux_lsb + @platform[:release] = lsb[:release] + true + end + } -# genaric linux -# this should always be last in the linux family list -plat.name('linux').title('Genaric Linux').in_family('linux') - .detect { - true - } + # genaric linux + # this should always be last in the linux family list + plat.name('linux').title('Genaric Linux').in_family('linux') + .detect { + true + } -# openvms -plat.name('openvms').title('OpenVMS').in_family('unix') - .detect { - if unix_uname_s =~ /unrecognized command verb/i - cmd = @backend.run_command('show system/noprocess') - unless cmd.exit_status != 0 || cmd.stdout.empty? - @platform[:name] = cmd.stdout.downcase.split(' ')[0] - cmd = @backend.run_command('write sys$output f$getsyi("VERSION")') - @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1] - cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")') - @platform[:arch] = cmd.stdout.downcase.split("\n")[1] - true - end - end - } + # openvms + plat.name('openvms').title('OpenVMS').in_family('unix') + .detect { + if unix_uname_s =~ /unrecognized command verb/i + cmd = @backend.run_command('show system/noprocess') + unless cmd.exit_status != 0 || cmd.stdout.empty? + @platform[:name] = cmd.stdout.downcase.split(' ')[0] + cmd = @backend.run_command('write sys$output f$getsyi("VERSION")') + @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1] + cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")') + @platform[:arch] = cmd.stdout.downcase.split("\n")[1] + true + end + end + } -# arista_eos family -plat.family('arista_eos').title('Arista EOS Family').in_family('unix') - .detect { - # we need a better way to determin this family - # for now we are going to just try each platform - true - } -plat.name('arista_eos').title('Arista EOS').in_family('arista_eos') - .detect { - cmd = @backend.run_command('show version | json') - if cmd.exit_status == 0 && !cmd.stdout.empty? - require 'json' - eos_ver = JSON.parse(cmd.stdout) - @platform[:release] = eos_ver['version'] - @platform[:arch] = eos_ver['architecture'] - true - end - } -plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') - .detect { - if unix_file_exist?('/usr/bin/FastCli') - cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') - if cmd.exit_status == 0 && !cmd.stdout.empty? - require 'json' - eos_ver = JSON.parse(cmd.stdout) - @platform[:release] = eos_ver['version'] - @platform[:arch] = eos_ver['architecture'] - true - end - end - } + # arista_eos family + plat.family('arista_eos').title('Arista EOS Family').in_family('unix') + .detect { + # we need a better way to determin this family + # for now we are going to just try each platform + true + } + plat.name('arista_eos').title('Arista EOS').in_family('arista_eos') + .detect { + cmd = @backend.run_command('show version | json') + if cmd.exit_status == 0 && !cmd.stdout.empty? + require 'json' + eos_ver = JSON.parse(cmd.stdout) + @platform[:release] = eos_ver['version'] + @platform[:arch] = eos_ver['architecture'] + true + end + } + plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') + .detect { + if unix_file_exist?('/usr/bin/FastCli') + cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') + if cmd.exit_status == 0 && !cmd.stdout.empty? + require 'json' + eos_ver = JSON.parse(cmd.stdout) + @platform[:release] = eos_ver['version'] + @platform[:arch] = eos_ver['architecture'] + true + end + end + } -# esx -plat.family('esx').title('ESXi Family') - .detect { - true if unix_uname_s =~ /vmkernel/i - } -plat.name('vmkernel').in_family('esx') - .detect { - @platform[:name] = unix_uname_s.lines[0].chomp - @platform[:release] = unix_uname_r.lines[0].chomp - true - } + # esx + plat.family('esx').title('ESXi Family') + .detect { + true if unix_uname_s =~ /vmkernel/i + } + plat.name('vmkernel').in_family('esx') + .detect { + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp + true + } -# aix -plat.family('aix').in_family('unix') - .detect { - true if unix_uname_s =~ /aix/ - } -plat.name('aix').title('Aix').in_family('aix') - .detect { - out = @backend.run_command('uname -rvp').stdout - m = out.match(/(\d+)\s+(\d+)\s+(.*)/) - unless m.nil? - @platform[:release] = "#{m[2]}.#{m[1]}" - @platform[:arch] = m[3].to_s - end - true - } + # aix + plat.family('aix').in_family('unix') + .detect { + true if unix_uname_s =~ /aix/ + } + plat.name('aix').title('Aix').in_family('aix') + .detect { + out = @backend.run_command('uname -rvp').stdout + m = out.match(/(\d+)\s+(\d+)\s+(.*)/) + unless m.nil? + @platform[:release] = "#{m[2]}.#{m[1]}" + @platform[:arch] = m[3].to_s + end + true + } -# solaris family -plat.family('solaris').in_family('unix') - .detect { - if unix_uname_s =~ /sunos/i - unless (version = /^5\.(?\d+)$/.match(uname_r)).nil? - @platform[:release] = version['release'] - end + # solaris family + plat.family('solaris').in_family('unix') + .detect { + if unix_uname_s =~ /sunos/i + unless (version = /^5\.(?\d+)$/.match(uname_r)).nil? + @platform[:release] = version['release'] + end - arch = @backend.run_command('uname -p') - @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0 - true - end - } -plat.name('smartos').title('SmartOS').in_family('solaris') - .detect { - rel = unix_file_contents('/etc/release') - if /^.*(SmartOS).*$/ =~ rel - true - end - } -plat.name('omnios').title('Omnios').in_family('solaris') - .detect { - rel = unix_file_contents('/etc/release') - if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil? - @platform[:release] = m[2] - true - end - } -plat.name('openindiana').title('Openindiana').in_family('solaris') - .detect { - rel = unix_file_contents('/etc/release') - if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil? - @platform[:release] = m[2] - true - end - } -plat.name('opensolaris').title('Open Solaris').in_family('solaris') - .detect { - rel = unix_file_contents('/etc/release') - if /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel - @platform[:release] = m[2] - true - end - } -plat.name('nexentacore').title('Nexentacore').in_family('solaris') - .detect { - rel = unix_file_contents('/etc/release') - if /^\s*(NexentaCore)\s.*$/ =~ rel - true - end - } -plat.name('solaris').title('Solaris').in_family('solaris') - .detect { - rel = unix_file_contents('/etc/release') - if !(m = /Oracle Solaris (\d+)/.match(rel)).nil? - # TODO: should be string! - @platform[:release] = m[1] - true - elsif /^\s*(Solaris)\s.*$/ =~ rel - true - end + arch = @backend.run_command('uname -p') + @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0 + true + end + } + plat.name('smartos').title('SmartOS').in_family('solaris') + .detect { + rel = unix_file_contents('/etc/release') + if /^.*(SmartOS).*$/ =~ rel + true + end + } + plat.name('omnios').title('Omnios').in_family('solaris') + .detect { + rel = unix_file_contents('/etc/release') + if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil? + @platform[:release] = m[2] + true + end + } + plat.name('openindiana').title('Openindiana').in_family('solaris') + .detect { + rel = unix_file_contents('/etc/release') + if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil? + @platform[:release] = m[2] + true + end + } + plat.name('opensolaris').title('Open Solaris').in_family('solaris') + .detect { + rel = unix_file_contents('/etc/release') + if /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel + @platform[:release] = m[2] + true + end + } + plat.name('nexentacore').title('Nexentacore').in_family('solaris') + .detect { + rel = unix_file_contents('/etc/release') + if /^\s*(NexentaCore)\s.*$/ =~ rel + true + end + } + plat.name('solaris').title('Solaris').in_family('solaris') + .detect { + rel = unix_file_contents('/etc/release') + if !(m = /Oracle Solaris (\d+)/.match(rel)).nil? + # TODO: should be string! + @platform[:release] = m[1] + true + elsif /^\s*(Solaris)\s.*$/ =~ rel + true + end - # must be some unknown solaris - true - } + # must be some unknown solaris + true + } -# hpux -plat.family('hpux').in_family('unix') - .detect { - true if unix_uname_s =~ /hp-ux/ - } -plat.name('hpux').title('Hpux').in_family('hpux') - .detect { - @platform[:release] = unix_uname_r.lines[0].chomp - true - } + # hpux + plat.family('hpux').in_family('unix') + .detect { + true if unix_uname_s =~ /hp-ux/ + } + plat.name('hpux').title('Hpux').in_family('hpux') + .detect { + @platform[:release] = unix_uname_r.lines[0].chomp + true + } -# bsd family -plat.family('bsd').in_family('unix') - .detect { - # we need a better way to determin this family - # for now we are going to just try each platform - true - } -plat.name('darwin').title('Darwin').in_family('bsd') - .detect { - if unix_uname_s =~ /darwin/ - @platform[:name] = unix_uname_s.lines[0].chomp - @platform[:release] = unix_uname_r.lines[0].chomp - true - end - cmd = @backend.run_command('/usr/bin/sw_vers') - return nil if cmd.exit_status != 0 || cmd.stdout.empty? + # bsd family + plat.family('bsd').in_family('unix') + .detect { + # we need a better way to determin this family + # for now we are going to just try each platform + true + } + plat.name('darwin').title('Darwin').in_family('bsd') + .detect { + if unix_uname_s =~ /darwin/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp + true + end + cmd = @backend.run_command('/usr/bin/sw_vers') + return nil if cmd.exit_status != 0 || cmd.stdout.empty? - @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1] - @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1] - @platform[:arch] = unix_uname_m - true - } -plat.name('freebsd').title('Freebsd').in_family('bsd') - .detect { - if unix_uname_s =~ /freebsd/ - @platform[:name] = unix_uname_s.lines[0].chomp - @platform[:release] = unix_uname_r.lines[0].chomp - true - end - } -plat.name('openbsd').title('Openbsd').in_family('bsd') - .detect { - if unix_uname_s =~ /openbsd/ - @platform[:name] = unix_uname_s.lines[0].chomp - @platform[:release] = unix_uname_r.lines[0].chomp - true - end - } -plat.name('netbsd').title('Netbsd').in_family('bsd') - .detect { - if unix_uname_s =~ /netbsd/ - @platform[:name] = unix_uname_s.lines[0].chomp - @platform[:release] = unix_uname_r.lines[0].chomp - true - end - } + @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1] + @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1] + @platform[:arch] = unix_uname_m + true + } + plat.name('freebsd').title('Freebsd').in_family('bsd') + .detect { + if unix_uname_s =~ /freebsd/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp + true + end + } + plat.name('openbsd').title('Openbsd').in_family('bsd') + .detect { + if unix_uname_s =~ /openbsd/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp + true + end + } + plat.name('netbsd').title('Netbsd').in_family('bsd') + .detect { + if unix_uname_s =~ /netbsd/ + @platform[:name] = unix_uname_s.lines[0].chomp + @platform[:release] = unix_uname_r.lines[0].chomp + true + end + } + end + end +end diff --git a/lib/train/transports/docker.rb b/lib/train/transports/docker.rb index c63dfd7f..c39d34cf 100644 --- a/lib/train/transports/docker.rb +++ b/lib/train/transports/docker.rb @@ -56,6 +56,7 @@ def reuse_connection class Train::Transports::Docker class Connection < BaseConnection def initialize(conf) + Train::Platforms::Specifications::OS.load_specifications super(conf) @id = options[:host] @container = ::Docker::Container.get(@id) || diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index 9d550322..84a5b029 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -18,6 +18,7 @@ def connection(_ = nil) class Connection < BaseConnection def initialize(options) + Train::Platforms::Specifications::OS.load_specifications super(options) @cmd_wrapper = nil @cmd_wrapper = CommandWrapper.load(self, options) diff --git a/lib/train/transports/mock.rb b/lib/train/transports/mock.rb index 6d05fae6..9767f4f7 100644 --- a/lib/train/transports/mock.rb +++ b/lib/train/transports/mock.rb @@ -61,6 +61,7 @@ class Connection < BaseConnection attr_reader :os def initialize(conf = nil) + Train::Platforms::Specifications::OS.load_specifications super(conf) @os = mock_os({}) @commands = {} diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index c0f070c0..19ae62dc 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -32,6 +32,7 @@ class Train::Transports::SSH class Connection < BaseConnection # rubocop:disable Metrics/ClassLength attr_reader :hostname def initialize(options) + Train::Platforms::Specifications::OS.load_specifications super(options) @username = @options.delete(:username) @hostname = @options.delete(:hostname) diff --git a/lib/train/transports/winrm_connection.rb b/lib/train/transports/winrm_connection.rb index 4f1f704e..196699d9 100644 --- a/lib/train/transports/winrm_connection.rb +++ b/lib/train/transports/winrm_connection.rb @@ -30,6 +30,7 @@ class Train::Transports::WinRM class Connection < BaseConnection # rubocop:disable Metrics/ClassLength attr_reader :hostname def initialize(options) + Train::Platforms::Specifications::OS.load_specifications super(options) @hostname = @options.delete(:hostname) @rdp_port = @options.delete(:rdp_port) From 453a51dda86a81bfc117294b85daf31e591c2101 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Wed, 1 Nov 2017 13:29:10 -0400 Subject: [PATCH 17/22] move around platform files into more logical spots Signed-off-by: Jared Quick --- lib/train/platforms.rb | 6 ++++-- lib/train/platforms/common.rb | 11 ++++++++--- lib/train/platforms/detect.rb | 3 --- .../platforms/detect/{ => helpers}/os_common.rb | 10 +++++----- .../platforms/detect/{ => helpers}/os_linux.rb | 2 +- .../detect/{ => helpers}/os_windows.rb | 2 +- lib/train/platforms/detect/scanner.rb | 8 +++----- .../platforms/{ => detect}/specifications/os.rb | 6 +++--- lib/train/{ => platforms}/platform.rb | 2 +- lib/train/plugins/base_connection.rb | 17 +++++------------ lib/train/transports/docker.rb | 8 -------- lib/train/transports/local.rb | 8 -------- lib/train/transports/mock.rb | 1 - lib/train/transports/ssh_connection.rb | 8 -------- lib/train/transports/winrm_connection.rb | 8 -------- test/unit/platforms/detect/os_common_test.rb | 2 +- test/unit/platforms/detect/os_linux_test.rb | 2 +- test/unit/platforms/detect/os_windows_test.rb | 2 +- test/unit/platforms/os_detect_test.rb | 2 +- 19 files changed, 35 insertions(+), 73 deletions(-) rename lib/train/platforms/detect/{ => helpers}/os_common.rb (83%) rename lib/train/platforms/detect/{ => helpers}/os_linux.rb (98%) rename lib/train/platforms/detect/{ => helpers}/os_windows.rb (98%) rename lib/train/platforms/{ => detect}/specifications/os.rb (99%) rename lib/train/{ => platforms}/platform.rb (98%) diff --git a/lib/train/platforms.rb b/lib/train/platforms.rb index 513179b5..403181e3 100644 --- a/lib/train/platforms.rb +++ b/lib/train/platforms.rb @@ -2,8 +2,10 @@ require 'train/platforms/common' require 'train/platforms/family' -require 'train/platform' +require 'train/platforms/platform' require 'train/platforms/detect' +require 'train/platforms/detect/scanner' +require 'train/platforms/detect/specifications/os' module Train::Platforms class << self @@ -34,7 +36,7 @@ def self.name(name, condition = {}) return plat end - Train::Platform.new(name, condition) + Train::Platforms::Platform.new(name, condition) end # Create or update a family diff --git a/lib/train/platforms/common.rb b/lib/train/platforms/common.rb index 2cf2ba53..0ce71357 100644 --- a/lib/train/platforms/common.rb +++ b/lib/train/platforms/common.rb @@ -19,9 +19,14 @@ def in_family(family) end def detect(&block) - return @detect unless block_given? - @detect = block - self + if block_given? + @detect = block + self + elsif @detect.nil? + ->(_) { false } + else + @detect + end end end end diff --git a/lib/train/platforms/detect.rb b/lib/train/platforms/detect.rb index f7cdd31a..5d0031e4 100644 --- a/lib/train/platforms/detect.rb +++ b/lib/train/platforms/detect.rb @@ -1,8 +1,5 @@ # encoding: utf-8 -require 'train/platforms/specifications/os' -require 'train/platforms/detect/scanner' - module Train::Platforms module Detect # Main detect method to scan all platforms for a match diff --git a/lib/train/platforms/detect/os_common.rb b/lib/train/platforms/detect/helpers/os_common.rb similarity index 83% rename from lib/train/platforms/detect/os_common.rb rename to lib/train/platforms/detect/helpers/os_common.rb index c9d38667..997fb9c7 100644 --- a/lib/train/platforms/detect/os_common.rb +++ b/lib/train/platforms/detect/helpers/os_common.rb @@ -1,13 +1,13 @@ # encoding: utf-8 -require 'train/platforms/detect/os_linux' -require 'train/platforms/detect/os_windows' +require 'train/platforms/detect/helpers/os_linux' +require 'train/platforms/detect/helpers/os_windows' require 'rbconfig' -module Train::Platforms::Detect +module Train::Platforms::Detect::Helpers module OSCommon - include Train::Platforms::Detect::Linux - include Train::Platforms::Detect::Windows + include Train::Platforms::Detect::Helpers::Linux + include Train::Platforms::Detect::Helpers::Windows def ruby_host_os(regex) ::RbConfig::CONFIG['host_os'] =~ regex diff --git a/lib/train/platforms/detect/os_linux.rb b/lib/train/platforms/detect/helpers/os_linux.rb similarity index 98% rename from lib/train/platforms/detect/os_linux.rb rename to lib/train/platforms/detect/helpers/os_linux.rb index 89246dac..4bfe3f5b 100644 --- a/lib/train/platforms/detect/os_linux.rb +++ b/lib/train/platforms/detect/helpers/os_linux.rb @@ -1,6 +1,6 @@ # encoding: utf-8 -module Train::Platforms::Detect +module Train::Platforms::Detect::Helpers module Linux def redhatish_platform(conf) conf =~ /^red hat/i ? 'redhat' : /(\w+)/i.match(conf)[1].downcase diff --git a/lib/train/platforms/detect/os_windows.rb b/lib/train/platforms/detect/helpers/os_windows.rb similarity index 98% rename from lib/train/platforms/detect/os_windows.rb rename to lib/train/platforms/detect/helpers/os_windows.rb index 153d3014..4b21ce26 100644 --- a/lib/train/platforms/detect/os_windows.rb +++ b/lib/train/platforms/detect/helpers/os_windows.rb @@ -1,6 +1,6 @@ # encoding: utf-8 -module Train::Platforms::Detect +module Train::Platforms::Detect::Helpers module Windows def detect_windows res = @backend.run_command('cmd /c ver') diff --git a/lib/train/platforms/detect/scanner.rb b/lib/train/platforms/detect/scanner.rb index 081ad33a..9427d0f0 100644 --- a/lib/train/platforms/detect/scanner.rb +++ b/lib/train/platforms/detect/scanner.rb @@ -1,10 +1,10 @@ # encoding: utf-8 -require 'train/platforms/detect/os_common' +require 'train/platforms/detect/helpers/os_common' module Train::Platforms::Detect class Scanner - include Train::Platforms::Detect::OSCommon + include Train::Platforms::Detect::Helpers::OSCommon def initialize(backend) @backend = backend @@ -24,7 +24,6 @@ def scan # start with the platform/families who have no families (the top levels) top = Train::Platforms.top_platforms top.each do |_name, plat| - next unless plat.detect next unless instance_eval(&plat.detect) == true # if we have a match start looking at the children @@ -41,10 +40,9 @@ def scan def scan_children(parent) parent.children.each do |plat, condition| - next if plat.detect.nil? next unless instance_eval(&plat.detect) == true - if plat.class == Train::Platform + if plat.class == Train::Platforms::Platform @platform[:family] = parent.name return plat if condition.empty? || check_condition(condition) elsif plat.class == Train::Platforms::Family diff --git a/lib/train/platforms/specifications/os.rb b/lib/train/platforms/detect/specifications/os.rb similarity index 99% rename from lib/train/platforms/specifications/os.rb rename to lib/train/platforms/detect/specifications/os.rb index 220855b8..f5b40938 100644 --- a/lib/train/platforms/specifications/os.rb +++ b/lib/train/platforms/detect/specifications/os.rb @@ -7,9 +7,9 @@ # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/PerceivedComplexity -module Train::Platforms::Specifications +module Train::Platforms::Detect::Specifications class OS - def self.load_specifications + def self.load plat = Train::Platforms plat.family('windows') @@ -111,7 +111,7 @@ def self.load_specifications end } # keep redhat after centos as a fallback for redhat base - plat.name('redhat').title('Red Hat Enterplat.ise Linux').in_family('redhat') + plat.name('redhat').title('Red Hat Linux').in_family('redhat') .detect { lsb = read_linux_lsb if lsb && lsb[:id] =~ /redhat/i diff --git a/lib/train/platform.rb b/lib/train/platforms/platform.rb similarity index 98% rename from lib/train/platform.rb rename to lib/train/platforms/platform.rb index 62425908..a86edf32 100644 --- a/lib/train/platform.rb +++ b/lib/train/platforms/platform.rb @@ -1,6 +1,6 @@ # encoding: utf-8 -module Train +module Train::Platforms class Platform include Train::Platforms::Common attr_accessor :backend, :condition, :families, :family_hierarchy, :platform diff --git a/lib/train/plugins/base_connection.rb b/lib/train/plugins/base_connection.rb index 8a079252..c2620071 100644 --- a/lib/train/plugins/base_connection.rb +++ b/lib/train/plugins/base_connection.rb @@ -1,8 +1,4 @@ # encoding: utf-8 -# -# Author:: Salim Afiune () -# Author:: Fletcher Nichol () -# Author:: Dominik Richter () require 'train/errors' require 'train/extras' @@ -30,6 +26,7 @@ def initialize(options = nil) @options = options || {} @logger = @options.delete(:logger) || Logger.new(STDOUT) @files = {} + Train::Platforms::Detect::Specifications::OS.load end # Closes the session connection, if it is still active. @@ -63,20 +60,16 @@ def run_command(_command) fail Train::ClientError, "#{self.class} does not implement #run_command()" end - # Get information on the operating system which this transport connects to. - # - # @return [OSCommon] operating system information - def os - fail Train::ClientError, "#{self.class} does not implement #os()" - end - # Get information on the operating system which this transport connects to. # # @return [Platform] system information def platform - fail Train::ClientError, "#{self.class} does not implement #platform()" + @platform ||= Train::Platforms::Detect.scan(self) end + # we need to keep os as a method for backwards compatibility with inspec + alias os platform + # Interact with files on the target. Read, write, and get metadata # from files via the transport. # diff --git a/lib/train/transports/docker.rb b/lib/train/transports/docker.rb index c39d34cf..75ce59b8 100644 --- a/lib/train/transports/docker.rb +++ b/lib/train/transports/docker.rb @@ -56,7 +56,6 @@ def reuse_connection class Train::Transports::Docker class Connection < BaseConnection def initialize(conf) - Train::Platforms::Specifications::OS.load_specifications super(conf) @id = options[:host] @container = ::Docker::Container.get(@id) || @@ -70,13 +69,6 @@ def close # nothing to do at the moment end - def platform - @platform ||= Train::Platforms::Detect.scan(self) - end - - # we need to keep os as a method for backwards compatibility with inspec - alias os platform - def file(path) @files[path] ||=\ if os.aix? diff --git a/lib/train/transports/local.rb b/lib/train/transports/local.rb index 84a5b029..1eeac343 100644 --- a/lib/train/transports/local.rb +++ b/lib/train/transports/local.rb @@ -18,7 +18,6 @@ def connection(_ = nil) class Connection < BaseConnection def initialize(options) - Train::Platforms::Specifications::OS.load_specifications super(options) @cmd_wrapper = nil @cmd_wrapper = CommandWrapper.load(self, options) @@ -37,13 +36,6 @@ def local? true end - def platform - @platform ||= Train::Platforms::Detect.scan(self) - end - - # we need to keep os as a method for backwards compatibility with inspec - alias os platform - def file(path) @files[path] ||= \ if os.windows? diff --git a/lib/train/transports/mock.rb b/lib/train/transports/mock.rb index 9767f4f7..6d05fae6 100644 --- a/lib/train/transports/mock.rb +++ b/lib/train/transports/mock.rb @@ -61,7 +61,6 @@ class Connection < BaseConnection attr_reader :os def initialize(conf = nil) - Train::Platforms::Specifications::OS.load_specifications super(conf) @os = mock_os({}) @commands = {} diff --git a/lib/train/transports/ssh_connection.rb b/lib/train/transports/ssh_connection.rb index 19ae62dc..5e996796 100644 --- a/lib/train/transports/ssh_connection.rb +++ b/lib/train/transports/ssh_connection.rb @@ -32,7 +32,6 @@ class Train::Transports::SSH class Connection < BaseConnection # rubocop:disable Metrics/ClassLength attr_reader :hostname def initialize(options) - Train::Platforms::Specifications::OS.load_specifications super(options) @username = @options.delete(:username) @hostname = @options.delete(:hostname) @@ -56,13 +55,6 @@ def close @session = nil end - def platform - @platform ||= Train::Platforms::Detect.scan(self) - end - - # we need to keep os as a method for backwards compatibility with inspec - alias os platform - def file(path) @files[path] ||= \ if os.aix? diff --git a/lib/train/transports/winrm_connection.rb b/lib/train/transports/winrm_connection.rb index 196699d9..b856d743 100644 --- a/lib/train/transports/winrm_connection.rb +++ b/lib/train/transports/winrm_connection.rb @@ -30,7 +30,6 @@ class Train::Transports::WinRM class Connection < BaseConnection # rubocop:disable Metrics/ClassLength attr_reader :hostname def initialize(options) - Train::Platforms::Specifications::OS.load_specifications super(options) @hostname = @options.delete(:hostname) @rdp_port = @options.delete(:rdp_port) @@ -47,13 +46,6 @@ def close @session = nil end - def platform - @platform ||= Train::Platforms::Detect.scan(self) - end - - # we need to keep os as a method for backwards compatibility with inspec - alias os platform - def file(path) @files[path] ||= Train::File::Remote::Windows.new(self, path) end diff --git a/test/unit/platforms/detect/os_common_test.rb b/test/unit/platforms/detect/os_common_test.rb index 5566e18f..b48f9861 100644 --- a/test/unit/platforms/detect/os_common_test.rb +++ b/test/unit/platforms/detect/os_common_test.rb @@ -4,7 +4,7 @@ class OsDetectLinuxTester attr_reader :platform - include Train::Platforms::Detect::OSCommon + include Train::Platforms::Detect::Helpers::OSCommon def initialize @platform = {} diff --git a/test/unit/platforms/detect/os_linux_test.rb b/test/unit/platforms/detect/os_linux_test.rb index 5f41cc63..bbe42ba1 100644 --- a/test/unit/platforms/detect/os_linux_test.rb +++ b/test/unit/platforms/detect/os_linux_test.rb @@ -4,7 +4,7 @@ require 'train/transports/mock' class OsDetectLinuxTester - include Train::Platforms::Detect::OSCommon + include Train::Platforms::Detect::Helpers::OSCommon end describe 'os_linux' do diff --git a/test/unit/platforms/detect/os_windows_test.rb b/test/unit/platforms/detect/os_windows_test.rb index 4052e318..1cd1a3ab 100644 --- a/test/unit/platforms/detect/os_windows_test.rb +++ b/test/unit/platforms/detect/os_windows_test.rb @@ -5,7 +5,7 @@ class OsDetectWindowsTester attr_reader :platform, :backend - include Train::Platforms::Detect::Windows + include Train::Platforms::Detect::Helpers::Windows def initialize @platform = {} diff --git a/test/unit/platforms/os_detect_test.rb b/test/unit/platforms/os_detect_test.rb index edb4401e..5f043617 100644 --- a/test/unit/platforms/os_detect_test.rb +++ b/test/unit/platforms/os_detect_test.rb @@ -3,7 +3,7 @@ require 'train/transports/mock' class OsDetectLinuxTester - include Train::Platforms::Detect::OSCommon + include Train::Platforms::Detect::Helpers::OSCommon end describe 'os_detect_linux' do From 98c10dd420138b8321b23bd33faf3ee7ea811aa6 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Wed, 1 Nov 2017 14:34:48 -0400 Subject: [PATCH 18/22] move redhat check to be at the end of the family as a catchall Signed-off-by: Jared Quick --- .../platforms/detect/specifications/os.rb | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/train/platforms/detect/specifications/os.rb b/lib/train/platforms/detect/specifications/os.rb index f5b40938..fe0550e4 100644 --- a/lib/train/platforms/detect/specifications/os.rb +++ b/lib/train/platforms/detect/specifications/os.rb @@ -110,20 +110,6 @@ def self.load true end } - # keep redhat after centos as a fallback for redhat base - plat.name('redhat').title('Red Hat Linux').in_family('redhat') - .detect { - lsb = read_linux_lsb - if lsb && lsb[:id] =~ /redhat/i - @platform[:release] = lsb[:release] - true - elsif !(raw = unix_file_contents('/etc/redhat-release')).nil? - # must be some type of redhat - @platform[:name] = redhatish_platform(raw) - @platform[:release] = redhatish_version(raw) - true - end - } plat.name('oracle').title('Oracle Linux').in_family('redhat') .detect { if !(raw = unix_file_contents('/etc/oracle-release')).nil? @@ -177,6 +163,20 @@ def self.load true end } + # keep redhat at the end as a fallback for anything with a redhat-release + plat.name('redhat').title('Red Hat Linux').in_family('redhat') + .detect { + lsb = read_linux_lsb + if lsb && lsb[:id] =~ /redhat/i + @platform[:release] = lsb[:release] + true + elsif !(raw = unix_file_contents('/etc/redhat-release')).nil? + # must be some type of redhat + @platform[:name] = redhatish_platform(raw) + @platform[:release] = redhatish_version(raw) + true + end + } # suse family plat.family('suse').in_family('linux') From 64a0ccd24f54f26cb76ab97e0a4d52f54780b05d Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Wed, 1 Nov 2017 15:03:06 -0400 Subject: [PATCH 19/22] update raspbian detect to also check in os_release Signed-off-by: Jared Quick --- lib/train/platforms/detect/specifications/os.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/train/platforms/detect/specifications/os.rb b/lib/train/platforms/detect/specifications/os.rb index fe0550e4..ca4ccf4e 100644 --- a/lib/train/platforms/detect/specifications/os.rb +++ b/lib/train/platforms/detect/specifications/os.rb @@ -62,7 +62,8 @@ def self.load } plat.name('raspbian').title('Raspbian Linux').in_family('debian') .detect { - if unix_file_exist?('/usr/bin/raspi-config') + if (linux_os_release && linux_os_release['NAME'] =~ /raspbian/i) || \ + unix_file_exist?('/usr/bin/raspi-config') @platform[:release] = unix_file_contents('/etc/debian_version').chomp true end From 42e6dff62a3898e942ccf18fff2a8ecd28666788 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Wed, 1 Nov 2017 15:16:18 -0400 Subject: [PATCH 20/22] update suse detect to be case insensitive Signed-off-by: Jared Quick --- lib/train/platforms/detect/specifications/os.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/train/platforms/detect/specifications/os.rb b/lib/train/platforms/detect/specifications/os.rb index ca4ccf4e..7624c6ab 100644 --- a/lib/train/platforms/detect/specifications/os.rb +++ b/lib/train/platforms/detect/specifications/os.rb @@ -191,11 +191,11 @@ def self.load } plat.name('opensuse').title('OpenSUSE Linux').in_family('suse') .detect { - true if unix_file_contents('/etc/SuSE-release') =~ /^openSUSE/ + true if unix_file_contents('/etc/SuSE-release') =~ /^opensuse/i } plat.name('suse').title('Suse Linux').in_family('suse') .detect { - true if unix_file_contents('/etc/SuSE-release') =~ /suse/ + true if unix_file_contents('/etc/SuSE-release') =~ /suse/i } # arch From 0425afb16d9002dd65689a04567254c9fa2c4b7c Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Wed, 1 Nov 2017 17:02:01 -0400 Subject: [PATCH 21/22] add helper comments and refactor transport test Signed-off-by: Jared Quick --- lib/train/platforms/common.rb | 2 ++ lib/train/platforms/detect/scanner.rb | 2 ++ test/unit/helper.rb | 1 - test/unit/transports/local_test.rb | 24 +++++++++++++----------- test/unit/transports/ssh_test.rb | 4 ++-- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/train/platforms/common.rb b/lib/train/platforms/common.rb index 0ce71357..44326c68 100644 --- a/lib/train/platforms/common.rb +++ b/lib/train/platforms/common.rb @@ -23,6 +23,8 @@ def detect(&block) @detect = block self elsif @detect.nil? + # we are returning a block that just returns false here + # to skip the family/platform evaluation if detect is not set ->(_) { false } else @detect diff --git a/lib/train/platforms/detect/scanner.rb b/lib/train/platforms/detect/scanner.rb index 9427d0f0..f5eb1abb 100644 --- a/lib/train/platforms/detect/scanner.rb +++ b/lib/train/platforms/detect/scanner.rb @@ -24,6 +24,8 @@ def scan # start with the platform/families who have no families (the top levels) top = Train::Platforms.top_platforms top.each do |_name, plat| + # we are doing a instance_eval here to make sure we have the proper + # context with all the detect helper methods next unless instance_eval(&plat.detect) == true # if we have a match start looking at the children diff --git a/test/unit/helper.rb b/test/unit/helper.rb index 40694c78..971f5bf1 100644 --- a/test/unit/helper.rb +++ b/test/unit/helper.rb @@ -5,4 +5,3 @@ require 'mocha/setup' require 'train' -require 'train/platforms/detect' diff --git a/test/unit/transports/local_test.rb b/test/unit/transports/local_test.rb index abceb97f..9669c82b 100644 --- a/test/unit/transports/local_test.rb +++ b/test/unit/transports/local_test.rb @@ -3,18 +3,20 @@ require 'helper' require 'train/transports/local' -$transport = nil -describe 'local transport' do - let(:transport) do - if $transport.nil? - plat = Train::Platforms.name('mock').in_family('linux') - plat.add_platform_methods - Train::Platforms::Detect.stubs(:scan).returns(plat) - $transport = Train::Transports::Local.new - end +class TransportHelper + attr_accessor :transport - $transport + def initialize + Train::Platforms::Detect::Specifications::OS.load + plat = Train::Platforms.name('mock').in_family('linux') + plat.add_platform_methods + Train::Platforms::Detect.stubs(:scan).returns(plat) + @transport = Train::Transports::Local.new end +end + +describe 'local transport' do + let(:transport) { TransportHelper.new.transport } let(:connection) { transport.connection } it 'can be instantiated' do @@ -26,7 +28,7 @@ end it 'provides a uri' do - connection.uri.must_equal "local://" + connection.uri.must_equal 'local://' end it 'doesnt wait to be read' do diff --git a/test/unit/transports/ssh_test.rb b/test/unit/transports/ssh_test.rb index 53e3cb3e..ebb4637f 100644 --- a/test/unit/transports/ssh_test.rb +++ b/test/unit/transports/ssh_test.rb @@ -4,12 +4,12 @@ require 'train/transports/ssh' describe 'ssh transport' do - let(:cls) { + let(:cls) do plat = Train::Platforms.name('mock').in_family('linux') plat.add_platform_methods Train::Platforms::Detect.stubs(:scan).returns(plat) Train::Transports::SSH - } + end let(:conf) {{ host: rand.to_s, password: rand.to_s, From babc87542b67a55a66e3e9aefb669a7f5fcefb56 Mon Sep 17 00:00:00 2001 From: Jared Quick Date: Thu, 9 Nov 2017 14:35:55 -0500 Subject: [PATCH 22/22] update detect logic for solaris and arista Signed-off-by: Jared Quick --- .../platforms/detect/specifications/os.rb | 96 +++++++++---------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/lib/train/platforms/detect/specifications/os.rb b/lib/train/platforms/detect/specifications/os.rb index 7624c6ab..62080ca0 100644 --- a/lib/train/platforms/detect/specifications/os.rb +++ b/lib/train/platforms/detect/specifications/os.rb @@ -33,6 +33,39 @@ def self.load end } + # arista_eos family + # this has to be before redhat as EOS is based off fedora + plat.family('arista_eos').title('Arista EOS Family').in_family('unix') + .detect { + # we need a better way to determin this family + # for now we are going to just try each platform + true + } + plat.name('arista_eos').title('Arista EOS').in_family('arista_eos') + .detect { + cmd = @backend.run_command('show version | json') + if cmd.exit_status == 0 && !cmd.stdout.empty? + require 'json' + eos_ver = JSON.parse(cmd.stdout) + @platform[:release] = eos_ver['version'] + @platform[:arch] = eos_ver['architecture'] + true + end + } + plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') + .detect { + if unix_file_exist?('/usr/bin/FastCli') + cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') + if cmd.exit_status == 0 && !cmd.stdout.empty? + require 'json' + eos_ver = JSON.parse(cmd.stdout) + @platform[:release] = eos_ver['version'] + @platform[:arch] = eos_ver['architecture'] + true + end + end + } + # linux master family plat.family('linux').in_family('unix') .detect { @@ -106,7 +139,7 @@ def self.load if lsb && lsb[:id] =~ /centos/i @platform[:release] = lsb[:release] true - elsif unix_file_contents('/etc/os-release') =~ /centos/i + elsif linux_os_release && linux_os_release['NAME'] =~ /centos/i @platform[:release] = redhatish_version(unix_file_contents('/etc/redhat-release')) true end @@ -158,7 +191,7 @@ def self.load if lsb && lsb[:id] =~ /amazon/i @platform[:release] = lsb[:release] true - elsif !(raw = unix_file_contents('/etc/system-release')).nil? + elsif (raw = unix_file_contents('/etc/system-release')) =~ /amazon/i @platform[:name] = redhatish_platform(raw) @platform[:release] = redhatish_version(raw) true @@ -280,37 +313,6 @@ def self.load end } - # arista_eos family - plat.family('arista_eos').title('Arista EOS Family').in_family('unix') - .detect { - # we need a better way to determin this family - # for now we are going to just try each platform - true - } - plat.name('arista_eos').title('Arista EOS').in_family('arista_eos') - .detect { - cmd = @backend.run_command('show version | json') - if cmd.exit_status == 0 && !cmd.stdout.empty? - require 'json' - eos_ver = JSON.parse(cmd.stdout) - @platform[:release] = eos_ver['version'] - @platform[:arch] = eos_ver['architecture'] - true - end - } - plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos') - .detect { - if unix_file_exist?('/usr/bin/FastCli') - cmd = @backend.run_command('FastCli -p 15 -c "show version | json"') - if cmd.exit_status == 0 && !cmd.stdout.empty? - require 'json' - eos_ver = JSON.parse(cmd.stdout) - @platform[:release] = eos_ver['version'] - @platform[:arch] = eos_ver['architecture'] - true - end - end - } # esx plat.family('esx').title('ESXi Family') @@ -327,7 +329,7 @@ def self.load # aix plat.family('aix').in_family('unix') .detect { - true if unix_uname_s =~ /aix/ + true if unix_uname_s =~ /aix/i } plat.name('aix').title('Aix').in_family('aix') .detect { @@ -344,7 +346,7 @@ def self.load plat.family('solaris').in_family('unix') .detect { if unix_uname_s =~ /sunos/i - unless (version = /^5\.(?\d+)$/.match(uname_r)).nil? + unless (version = /^5\.(?\d+)$/.match(unix_uname_r)).nil? @platform[:release] = version['release'] end @@ -379,7 +381,7 @@ def self.load plat.name('opensolaris').title('Open Solaris').in_family('solaris') .detect { rel = unix_file_contents('/etc/release') - if /^\s*(OpenSolaris).*snv_(\d+).*$/ =~ rel + if !(m = /^\s*(OpenSolaris).*snv_(\d+).*$/.match(rel)).nil? @platform[:release] = m[2] true end @@ -409,7 +411,7 @@ def self.load # hpux plat.family('hpux').in_family('unix') .detect { - true if unix_uname_s =~ /hp-ux/ + true if unix_uname_s =~ /hp-ux/i } plat.name('hpux').title('Hpux').in_family('hpux') .detect { @@ -426,22 +428,18 @@ def self.load } plat.name('darwin').title('Darwin').in_family('bsd') .detect { - if unix_uname_s =~ /darwin/ + cmd = unix_file_contents('/usr/bin/sw_vers') + if unix_uname_s =~ /darwin/i || !cmd.nil? @platform[:name] = unix_uname_s.lines[0].chomp - @platform[:release] = unix_uname_r.lines[0].chomp + @platform[:release] = cmd[/^ProductVersion:\s+(.+)$/, 1] || unix_uname_r.lines[0].chomp + @platform[:build] = cmd[/^BuildVersion:\s+(.+)$/, 1] + @platform[:arch] = unix_uname_m true end - cmd = @backend.run_command('/usr/bin/sw_vers') - return nil if cmd.exit_status != 0 || cmd.stdout.empty? - - @platform[:release] = cmd.stdout[/^ProductVersion:\s+(.+)$/, 1] - @platform[:build] = cmd.stdout[/^BuildVersion:\s+(.+)$/, 1] - @platform[:arch] = unix_uname_m - true } plat.name('freebsd').title('Freebsd').in_family('bsd') .detect { - if unix_uname_s =~ /freebsd/ + if unix_uname_s =~ /freebsd/i @platform[:name] = unix_uname_s.lines[0].chomp @platform[:release] = unix_uname_r.lines[0].chomp true @@ -449,7 +447,7 @@ def self.load } plat.name('openbsd').title('Openbsd').in_family('bsd') .detect { - if unix_uname_s =~ /openbsd/ + if unix_uname_s =~ /openbsd/i @platform[:name] = unix_uname_s.lines[0].chomp @platform[:release] = unix_uname_r.lines[0].chomp true @@ -457,7 +455,7 @@ def self.load } plat.name('netbsd').title('Netbsd').in_family('bsd') .detect { - if unix_uname_s =~ /netbsd/ + if unix_uname_s =~ /netbsd/i @platform[:name] = unix_uname_s.lines[0].chomp @platform[:release] = unix_uname_r.lines[0].chomp true