serverspec-runner-1.3.10/0000755000004100000410000000000014663174345015331 5ustar www-datawww-dataserverspec-runner-1.3.10/serverspec-runner.gemspec0000644000004100000410000000173514663174345022374 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'serverspec-runner/version' Gem::Specification.new do |spec| spec.name = "serverspec-runner" spec.version = ServerspecRunner::VERSION spec.authors = ["hiracy"] spec.email = ["leizhen@mbr.nifty.com"] spec.description = %q{simple execution framework for serverspec} spec.summary = %q{simple execution framework for serverspec} spec.homepage = "https://github.com/hiracy/serverspec-runner" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_runtime_dependency "serverspec" spec.add_runtime_dependency "net-ssh" spec.add_development_dependency "rake" spec.add_development_dependency "rspec-core" spec.add_development_dependency "bundler" end serverspec-runner-1.3.10/bin/0000755000004100000410000000000014663174345016101 5ustar www-datawww-dataserverspec-runner-1.3.10/bin/serverspec-runner0000755000004100000410000000737114663174345021527 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) require 'rubygems' require 'rake' require 'getoptlong' require 'serverspec-runner/version' raketask = 'spec' showtasks = false opts = GetoptLong.new( ["--scenario", "-s", GetoptLong::REQUIRED_ARGUMENT], ["--inventory", "-i", GetoptLong::REQUIRED_ARGUMENT], ["--specroot", "-r", GetoptLong::REQUIRED_ARGUMENT], ["--ssh_options", "-o", GetoptLong::REQUIRED_ARGUMENT], ["--explain", "-e", GetoptLong::REQUIRED_ARGUMENT], ["--tableformat", "-t", GetoptLong::REQUIRED_ARGUMENT], ["--parallel", "-p", GetoptLong::REQUIRED_ARGUMENT], ["--pattern", "-P", GetoptLong::REQUIRED_ARGUMENT], ["--exclude-pattern", "-E", GetoptLong::REQUIRED_ARGUMENT], ["--ignore-error-exit", "-I", GetoptLong::NO_ARGUMENT], ["--activate-specroot", "-a", GetoptLong::NO_ARGUMENT], ["--version", "-v", GetoptLong::NO_ARGUMENT], ["--help", "-h", GetoptLong::NO_ARGUMENT], ["--raketask", "-1", GetoptLong::REQUIRED_ARGUMENT], ["--tmpdir", "-2", GetoptLong::REQUIRED_ARGUMENT], ["--tasks", "-T", GetoptLong::NO_ARGUMENT] ) opts.each do |opt, arg| case opt when '--scenario' ENV['scenario'] = arg when '--inventory' ENV['inventory'] = arg when '--specroot' ENV['specroot'] = arg when '--ssh_options' ENV['ssh_options'] = arg when '--explain' ENV['explain'] = arg when '--tableformat' ENV['tableformat'] = arg when '--parallel' ENV['parallels'] = arg when '--pattern' ENV['pattern'] = arg when '--exclude-pattern' ENV['exclude_pattern'] = arg when '--ignore-error-exit' ENV['ignore_error_exit'] = 'true' when '--activate-specroot' ENV['activate_specroot'] = 'true' when '--tmpdir' ENV['tmpdir'] = arg when '--raketask' raketask = arg when '--tasks' showtasks = true when '--version' puts ServerspecRunner::VERSION exit 0 else puts "Usage: serverspec-runner (options)" puts "-s, --scenario SCENARIO_FILE path to scenario yml file" puts "-i, --inventory INVENTORY_FILE path to ansible inventory yml file" puts "-r, --specroot SPEC_ROOT path to spec tests root dir" puts "-o, --ssh_options SSH_OPTIONS_FILE path to ssh options yml file" puts "-e, --explain (short|long) specify result explain length(default: short)" puts "-t, --tableformat (aa|mkd|csv|bool|none) specify result table type(default: aa)" puts "-T, --tasks display the tasks with descriptions(exec rake -T)" puts "-p, --parallel execute tasks in parallel" puts "-P, --pattern execute pattern in spec directory" puts "-I, --ignore-error-exit exit with 0 even on error" puts "-a, --activate-specroot activate existed spec root only(--specroot is required)" puts "-1, --raketask RAKE_TASK_NAME execute specified rake task only(ex: spec:test::anyhost-01)" puts "-v, --version show version" puts "-h, --help show help" exit 0 end end Rake::TaskManager.record_task_metadata = showtasks load "#{File.dirname(__FILE__)}/../Rakefile" if showtasks mLen = (Rake.application.tasks.max_by { |t| t.name_with_args.size }).name_with_args.size Rake.application.tasks.reject{|t| ['spec:all', 'spec:stdout'].include?(t.name_with_args)}.each do |t| printf("%-#{mLen + 2}s # %s\n", t.name_with_args, t.comment) end exit(0) end Rake::Task[raketask.to_sym].invoke serverspec-runner-1.3.10/.gitignore0000644000004100000410000000032214663174345017316 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp .DS_Store .rvmrc _platforms.yml _serverspec_result.csv serverspec-runner-1.3.10/scenario.yml0000644000004100000410000000043114663174345017655 0ustar www-datawww-data# # test_dir_top: # test_dir_second_1: # - ssh-access-host-or-symbol # - : # role_dir_second_2: # - : # : # test_dir_hierarchies: # : # --- # ssh-access-host-or-symbol: # host: xxx.xxx.xxx.xxx # example: - anyhost-01 --- anyhost-01: host: 127.0.0.1 serverspec-runner-1.3.10/lib/0000755000004100000410000000000014663174345016077 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/0000755000004100000410000000000014663174345020113 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/serverspec/0000755000004100000410000000000014663174345022274 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/serverspec/type/0000755000004100000410000000000014663174345023255 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/serverspec/type/mysql.rb0000644000004100000410000000030714663174345024747 0ustar www-datawww-datamodule Serverspec::Type class Mysql < Base def replicated?(master=nil, user=nil, password=nil, port=nil) @runner.check_mysql_is_replicated(master, user, password, port) end end end serverspec-runner-1.3.10/lib/extension/serverspec/type/file.rb0000644000004100000410000000016414663174345024522 0ustar www-datawww-datamodule Serverspec::Type class File < Base def text? @runner.check_file_is_text(@name) end end end serverspec-runner-1.3.10/lib/extension/serverspec/helper/0000755000004100000410000000000014663174345023553 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/serverspec/helper/type.rb0000644000004100000410000000055314663174345025064 0ustar www-datawww-datamodule Serverspec module Helper module Type types = %w( mysql ) types.each {|type| require "extension/serverspec/type/#{type}" } types.each do |type| define_method type do |*args| name = args.first eval "Serverspec::Type::#{type.to_camel_case}.new(name)" end end end end end serverspec-runner-1.3.10/lib/extension/serverspec/matcher/0000755000004100000410000000000014663174345023717 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/serverspec/matcher/be_replicated.rb0000644000004100000410000000054614663174345027033 0ustar www-datawww-dataRSpec::Matchers.define :be_replicated do match do |host| host.replicated?(@master, @user, @password, @port) end chain :from do |master| @master = master end chain :with_user do |user| @user = user end chain :with_password do |password| @password = password end chain :with_port do |port| @port = port end end serverspec-runner-1.3.10/lib/extension/specinfra/0000755000004100000410000000000014663174345022065 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/specinfra/command/0000755000004100000410000000000014663174345023503 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/specinfra/command/base/0000755000004100000410000000000014663174345024415 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/specinfra/command/base/file.rb0000644000004100000410000000025614663174345025664 0ustar www-datawww-dataclass Specinfra::Command::Base::File < Specinfra::Command::Base class << self def check_is_text(file) "file #{escape(file)} | egrep ' text$'" end end end serverspec-runner-1.3.10/lib/extension/specinfra/command/linux/0000755000004100000410000000000014663174345024642 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/specinfra/command/linux/base/0000755000004100000410000000000014663174345025554 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/extension/specinfra/command/linux/base/mysql.rb0000644000004100000410000000111014663174345027237 0ustar www-datawww-dataclass Specinfra::Command::Linux::Base::Mysql < Specinfra::Command::Base class << self def check_is_replicated(master=nil, user=nil, password=nil, port=nil) opt_user = "--user=#{user} " || '' opt_password = "--password=#{password} " || '' opt_port = "--port=#{port} " || '' cmd = '' cmd += "echo 'show slave status \\G;' | mysql #{opt_user} #{opt_password} #{opt_port} | " cmd += "grep -e 'Slave_IO_Running: Yes' -e 'Slave_SQL_Running: Yes' -e 'Master_Host: #{master}' | " cmd += "wc -l | grep -w 3" cmd end end end serverspec-runner-1.3.10/lib/serverspec-runner/0000755000004100000410000000000014663174345021567 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/serverspec-runner/ansible/0000755000004100000410000000000014663174345023204 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/serverspec-runner/ansible/inventory.rb0000644000004100000410000000301314663174345025563 0ustar www-datawww-datamodule Inventory def self.inventory_to_platform(data) platform = {} data.each do |k, v| v.each do |gk, gv| if gk == 'hosts' if gv.kind_of?(Hash) gv.each do |hk, hv| platform[hk.to_sym] = unless hv.nil? convert_ssh_opt(v['vars']).merge(convert_ssh_opt(hv)) else convert_ssh_opt(v['vars']) end end elsif gv.kind_of?(Array) gv.each do |h| platform[h.to_sym] = convert_ssh_opt(v['vars']) end end end end end platform end private def self.convert_ssh_opt(src) serverspec_opt = {} serverspec_opt[:ssh_opts] = {} src.each do |k, v| case k when 'ansible_host' serverspec_opt[:host] = v when 'ansible_user' serverspec_opt[:ssh_opts][:user] = v when 'ansible_port' serverspec_opt[:ssh_opts][:port] = v when 'ansible_ssh_private_key_file' serverspec_opt[:ssh_opts][:keys] = Array(v) when 'ansible_ssh_pass' serverspec_opt[:ssh_opts][:password] = v when 'ansible_ssh_common_args' if v.include?('StrictHostKeyChecking=no') serverspec_opt[:ssh_opts][:verify_host_key] = false elsif v.include?('UserKnownHostsFile=/dev/null') serverspec_opt[:ssh_opts][:user_known_hosts_file] = '/dev/null' end end end serverspec_opt end end serverspec-runner-1.3.10/lib/serverspec-runner/util/0000755000004100000410000000000014663174345022544 5ustar www-datawww-dataserverspec-runner-1.3.10/lib/serverspec-runner/util/hash.rb0000644000004100000410000000053014663174345024012 0ustar www-datawww-dataclass Hash def depth 1 + (values.map{|v| Hash === v ? v.depth : 1}.max) end def deep_symbolize_keys self.each_with_object({}) do |(k, v), h| if v.is_a?(Array) v = v.map{|vv| vv.deep_symbolize_keys} elsif v.is_a?(Hash) v = v.deep_symbolize_keys end h[k.to_s.to_sym] = v end end end serverspec-runner-1.3.10/lib/serverspec-runner/version.rb0000644000004100000410000000006114663174345023576 0ustar www-datawww-datamodule ServerspecRunner VERSION = "1.3.10" end serverspec-runner-1.3.10/lib/serverspec-runner.rb0000644000004100000410000000004414663174345022112 0ustar www-datawww-datarequire 'serverspec-runner/version' serverspec-runner-1.3.10/spec/0000755000004100000410000000000014663174345016263 5ustar www-datawww-dataserverspec-runner-1.3.10/spec/spec_helper.rb0000644000004100000410000000671214663174345021107 0ustar www-datawww-datarequire 'serverspec' require 'pathname' require 'net/ssh' require 'net/ssh/proxy/command' require 'yaml' require 'csv' require 'serverspec-runner/util/hash' # require extension libraries Dir.glob([ ENV['specroot'] + '/lib/extension/serverspec/**/*.rb', ENV['specroot'] + '/lib/extension/specinfra/**/*.rb']).each {|f| require f} ssh_opts_default = YAML.load_file(ENV['ssh_options']) csv_path = ENV['result_csv'] explains = [] results = [] row_num = [] spacer_char = ' ' unless ENV['tableformat'] == 'csv' spacer_char = ',' if ENV['tableformat'] == 'csv' def get_example_desc(example_group, descriptions) descriptions << example_group[:description] return descriptions if example_group[:parent_example_group] == nil get_example_desc(example_group[:parent_example_group], descriptions) end RSpec.configure do |c| c.expose_current_running_example_as :example c.path = ENV['EXEC_PATH'] run_path = c.files_to_run[0].split('/') speck_i = 0 run_path.reverse.each_with_index do |r,i| if r == 'spec' speck_i = ((run_path.size - 1) - i) end end sliced = run_path.slice((speck_i + 1)..(run_path.size - 2)) role_name = sliced.join('/') if ENV['ASK_SUDO_PASSWORD'] require 'highline/import' c.sudo_password = ask("Enter sudo password: ") { |q| q.echo = false } else c.sudo_password = ENV['SUDO_PASSWORD'] end set_property (YAML.load_file(ENV['platforms_tmp']))[ENV['TARGET_HOST'].to_sym] if ENV['TARGET_CONNECTION'] == 'ssh' || ENV['TARGET_SSH_HOST'] !~ /localhost|127\.0\.0\.1/ c.host = ENV['TARGET_SSH_HOST'] options = Net::SSH::Config.for(c.host, files=["~/.ssh/config"]) ssh_opts ||= ssh_opts_default property[:ssh_opts].each { |k, v| ssh_opts[k.to_sym] = v } if property[:ssh_opts] ssh_opts[:proxy] = Kernel.eval(ssh_opts[:proxy]) if ssh_opts[:proxy] user = options[:user] || ssh_opts[:user] || Etc.getlogin options.merge!(ssh_opts) set :ssh_options, options set :backend, :ssh set :request_pty, true else set :backend, :exec end prev_desc_hierarchy = nil c.before(:suite) do entity_host = (((ENV['TARGET_HOST'] != ENV['TARGET_SSH_HOST']) && (ENV['TARGET_SSH_HOST'] != nil)) ? "(#{ENV['TARGET_SSH_HOST']})" : "") puts "\e[33m" puts "### start [#{role_name}@#{ENV['TARGET_HOST']}] #{entity_host} serverspec... ###" print "\e[m" explains << "#{role_name}@#{ENV['TARGET_HOST']}#{entity_host}" results << "" row_num << 1 end c.after(:each) do if ENV['explain'] == 'long' explains << spacer_char + example.metadata[:full_description] results << (self.example.exception ? 'NG' : 'OK') row_num << 1 else spacer = '' desc_hierarchy = get_example_desc(self.example.metadata[:example_group], []).reverse desc_hierarchy.each_with_index do |ex, i| spacer += spacer_char if prev_desc_hierarchy != nil && prev_desc_hierarchy.length > i && prev_desc_hierarchy[i] == desc_hierarchy[i] else explains << spacer + ex results << '' row_num << i + 1 end end explains << spacer + spacer_char + (self.example.metadata[:description] || '') results << (self.example.exception ? 'NG' : 'OK') row_num << desc_hierarchy.length + 1 prev_desc_hierarchy = desc_hierarchy end end c.after(:suite) do CSV.open(csv_path, 'a') do |writer| explains.each_with_index do |v, i| writer << [v, results[i], row_num[i]] end end end end serverspec-runner-1.3.10/spec/example/0000755000004100000410000000000014663174345017716 5ustar www-datawww-dataserverspec-runner-1.3.10/spec/example/default.rb0000644000004100000410000000077714663174345021702 0ustar www-datawww-datarequire 'spec_helper' describe user('root') do it { should exist } it { should have_uid 0 } it { should have_home_directory '/root' } end describe group('root') do it { should have_gid 0 } end describe 'Filesystem' do describe file('/') do it { should be_mounted } end end describe host('www.google.com') do it { should be_resolvable } it { should be_reachable } end describe command('dmesg | grep "FAIL\|Fail\|fail\|ERROR\|Error\|error"') do its(:exit_status){ should_not eq 0 } end serverspec-runner-1.3.10/spec/spec_helper_win.rb0000644000004100000410000000727714663174345021773 0ustar www-datawww-datarequire 'serverspec' require 'pathname' require 'net/ssh' require 'net/ssh/proxy/command' require 'yaml' require 'csv' require 'serverspec-runner/util/hash' require 'winrm' # require extension libraries Dir.glob([ ENV['specroot'] + '/lib/extension/serverspec/**/*.rb', ENV['specroot'] + '/lib/extension/specinfra/**/*.rb']).each {|f| require f} ssh_opts_default = YAML.load_file(ENV['ssh_options']) csv_path = ENV['result_csv'] explains = [] results = [] row_num = [] spacer_char = ' ' unless ENV['tableformat'] == 'csv' spacer_char = ',' if ENV['tableformat'] == 'csv' def get_example_desc(example_group, descriptions) descriptions << example_group[:description] return descriptions if example_group[:parent_example_group] == nil get_example_desc(example_group[:parent_example_group], descriptions) end RSpec.configure do |c| c.expose_current_running_example_as :example c.path = ENV['EXEC_PATH'] run_path = c.files_to_run[0].split('/') speck_i = 0 run_path.reverse.each_with_index do |r,i| if r == 'spec' speck_i = ((run_path.size - 1) - i) end end sliced = run_path.slice((speck_i + 1)..(run_path.size - 2)) role_name = sliced.join('/') if ENV['ASK_SUDO_PASSWORD'] require 'highline/import' c.sudo_password = ask("Enter sudo password: ") { |q| q.echo = false } else c.sudo_password = ENV['SUDO_PASSWORD'] end set_property (YAML.load_file(ENV['platforms_tmp']))[ENV['TARGET_HOST'].to_sym] if ENV['TARGET_SSH_HOST'] !~ /localhost|127\.0\.0\.1/ c.host = ENV['TARGET_SSH_HOST'] options = Net::SSH::Config.for(c.host, files=["~/.ssh/config"]) ssh_opts ||= ssh_opts_default property[:ssh_opts].each { |k, v| ssh_opts[k.to_sym] = v } if property[:ssh_opts] ssh_opts[:proxy] = Kernel.eval(ssh_opts[:proxy]) if ssh_opts[:proxy] user = options[:user] || ssh_opts[:user] || Etc.getlogin options.merge!(ssh_opts) set :ssh_options, options set :backend, :ssh set :request_pty, true else set :backend, :exec end prev_desc_hierarchy = nil c.before(:suite) do entity_host = (((ENV['TARGET_HOST'] != ENV['TARGET_SSH_HOST']) && (ENV['TARGET_SSH_HOST'] != nil)) ? "(#{ENV['TARGET_SSH_HOST']})" : "") puts "\e[33m" puts "### start [#{role_name}@#{ENV['TARGET_HOST']}] #{entity_host} serverspec... ###" print "\e[m" explains << "#{role_name}@#{ENV['TARGET_HOST']}#{entity_host}" results << "" row_num << 1 end c.after(:each) do if ENV['explain'] == 'long' explains << spacer_char + example.metadata[:full_description] results << (self.example.exception ? 'NG' : 'OK') row_num << 1 else spacer = '' desc_hierarchy = get_example_desc(self.example.metadata[:example_group], []).reverse desc_hierarchy.each_with_index do |ex, i| spacer += spacer_char if prev_desc_hierarchy != nil && prev_desc_hierarchy.length > i && prev_desc_hierarchy[i] == desc_hierarchy[i] else explains << spacer + ex results << '' row_num << i + 1 end end explains << spacer + spacer_char + (self.example.metadata[:description] || '') results << (self.example.exception ? 'NG' : 'OK') row_num << desc_hierarchy.length + 1 prev_desc_hierarchy = desc_hierarchy end end c.after(:suite) do CSV.open(csv_path, 'a') do |writer| explains.each_with_index do |v, i| writer << [v, results[i], row_num[i]] end end end end set :backend, :winrm opts = { user: "Username", # ex.) Administrator password: "Password", # connection password endpoint: "http://IPaddress:5985/wsman", operation_timeout: 300, } winrm = WinRM::Connection.new(opts) Specinfra.configuration.winrm = winrm serverspec-runner-1.3.10/spec/example_win/0000755000004100000410000000000014663174345020573 5ustar www-datawww-dataserverspec-runner-1.3.10/spec/example_win/default.rb0000644000004100000410000000052014663174345022541 0ustar www-datawww-datarequire 'spec_helper_win' describe command('hostname') do its(:stdout) { should match /HOSTNAME/ } end describe file('c:/windows') do it { should be_directory } end describe command('tzutil /g') do its(:stdout) { should match /Tokyo Standard Time/ } end describe windows_feature('notepad') do it{ should be_installed } end serverspec-runner-1.3.10/.rspec0000644000004100000410000000003714663174345016446 0ustar www-datawww-data--color --format documentation serverspec-runner-1.3.10/Rakefile0000644000004100000410000002167214663174345017006 0ustar www-datawww-datarequire 'rake' require 'rspec/core/rake_task' require 'yaml' require 'csv' require 'fileutils' require 'net/ssh' require 'open-uri' require 'serverspec-runner' require 'serverspec-runner/util/hash' require 'serverspec-runner/ansible/inventory' desc "Run serverspec to all scenario" task :spec => 'spec:all' namespace :spec do ENV['EXEC_PATH'] = '/usr/local/bin:/usr/sbin:/sbin:/usr/bin:/bin' if ENV['specroot'] == nil if ENV['scenario'] != nil ENV['specroot'] = "#{File.dirname(ENV['scenario'])}" else ENV['specroot'] = '.' end end Dir.chdir(ENV['specroot']) if Dir.exist?(ENV['specroot']) ENV['specpath'] = "#{ENV['specroot']}/spec" ENV['ssh_options'] = ENV['ssh_options'] || "#{ENV['specroot']}/ssh_options_default.yml" || "#{File.dirname(__FILE__)}/ssh_options_default.yml" ENV['ssh_options'] = "#{File.dirname(__FILE__)}/ssh_options_default.yml" unless File.exist?(ENV['ssh_options']) ssh_options = YAML.load_file(ENV['ssh_options']) ENV['result_csv'] = ENV['result_csv'] || './_serverspec_result.csv' csv_file = ENV['result_csv'] CSV.open(csv_file, 'w') { |w| w << ['description', 'result'] } ENV['explain'] = ENV['explain'] || "short" ENV['tableformat'] = ENV['tableformat'] || "aa" ENV['scenario'] = File.expand_path(ENV['scenario'] || "./scenario.yml") ENV['inventory'] = File.expand_path(ENV['inventory']) if ENV['inventory'] def init_specpath(path, only_activate) abs_path = File::expand_path(path) unless only_activate begin print "want to create spec-tree to #{abs_path}? (y/n): " ans = STDIN.gets.strip exit 0 unless (ans == 'y' || ans == 'yes') rescue Exception exit 0 end end FileUtils.mkdir_p("#{path}/lib") FileUtils.cp("#{File.dirname(__FILE__)}/scenario.yml", path) FileUtils.cp("#{File.dirname(__FILE__)}/ssh_options_default.yml", path) FileUtils.cp("#{File.dirname(__FILE__)}/.rspec", path) FileUtils.cp_r("#{File.dirname(__FILE__)}/spec", path) FileUtils.cp_r("#{File.dirname(__FILE__)}/lib/extension", "#{path}/lib") puts("Please edit \"#{abs_path}/scenario.yml\" and change directory to \"#{abs_path}\" and exec \"serverspec-runner\" command !!") end def gen_exec_plan(parent, node, path, ssh_options, tasks, platform) if parent == nil abs_node = node else abs_node = parent[node] end if abs_node.kind_of?(Hash) abs_node.keys.each do |n| path.push(n.to_s) gen_exec_plan(abs_node, n, path, ssh_options, tasks, platform) end path.pop elsif abs_node.kind_of?(Array) abs_node.each do |host_alias| if platform.include?(host_alias.to_sym) ssh_host = platform[host_alias.to_sym][:host] else platform[host_alias.to_sym] = {} ssh_host = host_alias end platform[host_alias.to_sym][:ssh_opts].each { |k, v| ssh_options[k.to_sym] = v } if platform[host_alias.to_sym].include?(:ssh_opts) tasks << "#{path.join('::')}::#{host_alias}" end path.pop end end def exec_tasks(parent, node, real_path, platform) spec_file_pattern = ENV['pattern'] || "**/*.rb" spec_file_exclude_pattern = ENV['exclude_pattern'] if parent == nil abs_node = node else abs_node = parent[node] end if abs_node.kind_of?(Hash) abs_node.keys.each do |n| real_path.push(n) exec_tasks(abs_node, n, real_path, platform) end real_path.pop elsif abs_node.kind_of?(Array) task_path = "#{real_path.join('::')}" abs_node.each do |host_alias| desc "Run serverspec to #{task_path}@#{host_alias}" RSpec::Core::RakeTask.new("#{task_path}::#{host_alias}".to_sym) do |t| fpath = task_path.gsub(/::/, '/') if Dir.exist?("#{ENV['specpath']}/#{fpath}") t.pattern = "#{ENV['specpath']}/#{fpath}/#{spec_file_pattern}" if spec_file_exclude_pattern t.exclude_pattern = "#{ENV['specpath']}/#{fpath}/#{spec_file_exclude_pattern}" end elsif File.file?("#{ENV['specpath']}/#{fpath}.rb") t.pattern = %W[ #{ENV['specpath']}/#{fpath}.rb ] end raise "\e[31mspec file not found!![#{t.pattern.to_s}]\e[m" if Dir.glob(t.pattern).empty? t.fail_on_error = false ENV['TARGET_HOST'] = host_alias ENV['TARGET_SSH_HOST'] = platform[host_alias.to_sym][:host] || host_alias if platform[host_alias.to_sym].key?(:ssh_opts) && platform[host_alias.to_sym][:ssh_opts].key?(:port) ENV['TARGET_CONNECTION'] = 'ssh' end end end real_path.pop end end if !Dir.exist?(ENV['specpath']) init_specpath(ENV['specroot'], false) exit 0 elsif ENV['activate_specroot'] init_specpath(ENV['specroot'], true) exit 0 end if !ENV['tmpdir'] ENV['platforms_tmp'] = "./_platforms.yml" ENV['scenario_tmp'] = "./_scenario.yml" else ENV['platforms_tmp'] = "#{ENV['tmpdir']}/_platforms.yml" ENV['scenario_tmp'] = "#{ENV['tmpdir']}/_scenario.yml" end scenarios = nil platform = {} if ENV['scenario'] =~ /^(http|https):\/\// open(ENV['scenario_tmp'], 'w') do |f| open(ENV['scenario']) do |data| f.write(data.read) end end ENV['scenario'] = ENV['scenario_tmp'] end if !File.file?(ENV['scenario']) && !File.file?("#{ENV['specroot']}/scenario.yml") print "\e[31m" puts "scenario.yml is not found.(--help option can display manual))" print "\e[m" exit 1 end File.open(ENV['scenario'] || "#{ENV['specroot']}/scenario.yml") do |f| YAML.load_stream(f).each_with_index do |data, idx| if idx == 0 scenarios = data else if data != nil data.each do |k, v| platform[k.to_sym] = v.deep_symbolize_keys end end end end end if ENV['inventory'] if !File.file?(ENV['inventory']) print "\e[31m" puts "inventory file is not found.(--help option can display manual))" print "\e[m" exit 1 end platform = Inventory.inventory_to_platform(YAML.load_file(ENV['inventory'])) end if !scenarios print "\e[31m" puts "scenario is empty." print "\e[m" exit 1 end tasks = [] gen_exec_plan(nil, scenarios, [], ssh_options, tasks, platform) task :stdout do ENV['is_example_error'] = 'true' if CSV.foreach(csv_file).any? { |c| c.size > 1 && c[1] == 'NG' } if ENV['tableformat'] == 'none' elsif ENV['tableformat'] == 'bool' ret = 'ok' CSV.foreach(csv_file) do |r| ret = 'ng' if r[1] == 'NG' end puts ret elsif ENV['tableformat'] == 'csv' maxrows = 0 CSV.foreach(csv_file) do |r| maxrows = r[2].to_i if r[2].to_i > maxrows end maxrows += 1 # host row CSV.foreach(csv_file) do |r| pad_comma = ',' * (maxrows - r[0].split(',').length) puts "#{r[0]}#{pad_comma},#{r[1]}" end else maxlen = 0 CSV.foreach(csv_file) do |r| n = r[0].each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+) maxlen = n if n > maxlen end pad_spaces = 4 spacer = nil if ENV['tableformat'] == 'mkd' spacer = "|:" + ("-" * maxlen) + "|:" + ("-" * "result".length) + ":|" elsif ENV['tableformat'] == 'aa' spacer = "+" + ("-" * (maxlen + "result".length + pad_spaces)) + "+" end puts spacer unless ENV['tableformat'] == 'mkd' is_header = true CSV.foreach(csv_file) do |r| n = r[0].each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+) if r[1] == 'OK' s_effect = "\e[32m" e_effect = "\e[m" r[1] = ' ' + r[1] elsif r[1] == 'NG' s_effect = "\e[31m" e_effect = "\e[m" r[1] = ' ' + r[1] end pad_mid = (" " * (maxlen - n)) + " | " pad_tail = (" " * ("result".length - r[1].length)) + " |" puts "|#{s_effect}#{r[0]}#{e_effect}#{pad_mid}#{s_effect}#{r[1]}#{e_effect}#{pad_tail}" if is_header puts spacer is_header = false end end puts spacer unless ENV['tableformat'] == 'mkd' end end task :exit do exit(1) if !ENV['ignore_error_exit'] && ENV['is_example_error'] end exec_tasks = [] if ENV['parallels'] processes = ENV['parallels'].to_i split_group = [] groups = 0 tasks.each_with_index do |t,pos| split_group << t if pos % processes == 0 || pos == tasks.length - 1 multitask "parallel_tasks_#{groups}".to_s => split_group groups += 1 split_group = [] end end groups.times {|i| exec_tasks << "parallel_tasks_#{i}" } else exec_tasks = tasks end exec_tasks << :stdout exec_tasks << :exit task :all => exec_tasks # tempファイルに書き出し open(ENV['platforms_tmp'] ,"w") do |y| YAML.dump(platform, y) end path = {} exec_tasks(nil, scenarios, [], platform) end serverspec-runner-1.3.10/Gemfile0000644000004100000410000000014614663174345016625 0ustar www-datawww-datasource "https://rubygems.org" # Specify your gem's dependencies in serverspec-runner.gemspec gemspec serverspec-runner-1.3.10/LICENSE0000644000004100000410000000206014663174345016334 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2014 hiracy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.serverspec-runner-1.3.10/.travis.yml0000644000004100000410000000022614663174345017442 0ustar www-datawww-datalanguage: ruby rvm: # - 1.9.3 - 2.0.0 - 2.1.5 - 2.2.0 - 2.5.1 before_install: - gem update bundler script: - bundle exec rake spec -I serverspec-runner-1.3.10/README.md0000644000004100000410000001426414663174345016617 0ustar www-datawww-dataserverspec-runner [![Gem Version](https://badge.fury.io/rb/serverspec-runner.svg)](http://badge.fury.io/rb/serverspec-runner) [![BuildStatus](https://secure.travis-ci.org/hiracy/serverspec-runner.png)](http://travis-ci.org/hiracy/serverspec-runner) ====================== Simple execution framework for [serverspec](http://serverspec.org/) run. ---- ## Installation $ gem install serverspec-runner ---- ## Usage initialize spec direcotries and create skeleton-specfiles. $ serverspec-runner -r /path/to/your_serverspec_root Edit your [spec-files](http://serverspec.org/resource_types.html). $ vim /path/to/your_serverspec_root/test_top_dir/.../your_serverspec_test.rb Edit your infrastructure or middleware tests scenario to "[scenario.yml](scenario.yml)". ``` test_top_dir: # test directory top : # test hierarchy directories test_bottom_dir: # test directory bottom - servername # ssh-accessible ip address or fqdn. or alias - : - : : --- servername: # alias name(not required) host: 192.168.0.11 # ssh-accessible ip address or fqdn(required if alias exist) ssh_opts: # ssh options(not required) port: 22 # ssh port option(not required) user: "anyone" # ssh user option(not required) keys: ["~/.ssh/id_rsa"] # ssh private keys option(not required) : # any other Net::SSH Options(not required) any_attribute: "aaa" # host attributes. this example available to get "property[:any_attribute]" from your codes(not required) : : ``` do tests. $ serverspec-runner -r /path/to/your_serverspec_root -s /path/to/your_scenario.yml or $ cd /path/to/your_serverspec_root && serverspec-runner You can also specify [ssh_options.yml](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html)(Net::SSH options) file by "-o" option for default ssh options. $ serverspec-runner -s /path/to/your_scenario.yml -o /path/to/your_ssh_options.yml For example. You write serverspec code like this. ``` require 'spec_helper' describe "nginx" do describe "check running" do describe process('nginx') do it { should be_running } end end describe file('/etc/logrotate.d/nginx') do it { should be_file } it { should contain "rotate 14" } end end ``` You can get the following outputs. * serverspec-runner -t aa : asci-art table(default) ``` +-------------------------------------------+ |description | result | +-------------------------------------------+ |example@anyhost-01(192.168.11.1) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | OK | |example@anyhost-02(192.168.11.2) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | NG | +-------------------------------------------+ ``` * serverspec-runner -t mkd : markdown table format ``` |description | result | |:---------------------------------|:------:| |example@anyhost-01(192.168.11.1) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | OK | |example@anyhost-02(192.168.11.2) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | NG | ``` this example parsed for markdown to that(use -e long option) |description | result | |:----------------------------------------------------------------|:------:| |example@anyhost-01(192.168.11.1) | | | nginx check running Process "nginx" should be running | OK | | nginx File "/etc/logrotate.d/nginx" should be file | OK | | nginx File "/etc/logrotate.d/nginx" should contain "rotate 14" | OK | |example@anyhost-01(192.168.11.2) | | | nginx check running Process "nginx" should be running | OK | | nginx File "/etc/logrotate.d/nginx" should be file | OK | | nginx File "/etc/logrotate.d/nginx" should contain "rotate 14" | NG | * serverspec-runner -t bool : only 'ok' or 'ng' string You can use for cluster monitoring system health. ``` ok ``` ``` ng ``` * serverspec-runner -t csv : CSV file format You can get result CSV format output and can use redirect to file. ``` description,,,,,result example@anyhost-01(192.168.11.1),,,,, ,nginx,,,, ,,check running,,, ,,,Process "nginx",, ,,,,should be running,OK ,,File "/etc/logrotate.d/nginx",,, ,,,should be file,,OK ,,,should contain "rotate 14",,OK example@anyhost-02(192.168.11.2),,,,, ,nginx,,,, ,,check running,,, ,,,Process "nginx",, ,,,,should be running,OK ,,File "/etc/logrotate.d/nginx",,, ,,,should be file,,OK ,,,should contain "rotate 14",,NG ``` For more detail. You can see from `serverspec-runner -h` command. ---- ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request serverspec-runner-1.3.10/ssh_options_default.yml0000644000004100000410000000016014663174345022125 0ustar www-datawww-data:port: 22 :paranoid: false #:user: "youre_name" #:keys: ["/path/to/private_key"] #:passphrase: "yourpassphrase"