serverspec-runner-1.3.8/0000755000004100000410000000000013410012544015235 5ustar www-datawww-dataserverspec-runner-1.3.8/.travis.yml0000644000004100000410000000022613410012544017346 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.8/.rspec0000644000004100000410000000003713410012544016352 0ustar www-datawww-data--color --format documentation serverspec-runner-1.3.8/README.md0000644000004100000410000001426413410012544016523 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.8/bin/0000755000004100000410000000000013410012544016005 5ustar www-datawww-dataserverspec-runner-1.3.8/bin/serverspec-runner0000755000004100000410000000737113410012544021433 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.8/spec/0000755000004100000410000000000013410012544016167 5ustar www-datawww-dataserverspec-runner-1.3.8/spec/spec_helper_win.rb0000644000004100000410000000727713410012544021677 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.8/spec/example/0000755000004100000410000000000013410012544017622 5ustar www-datawww-dataserverspec-runner-1.3.8/spec/example/default.rb0000644000004100000410000000077713410012544021606 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.8/spec/example_win/0000755000004100000410000000000013410012544020477 5ustar www-datawww-dataserverspec-runner-1.3.8/spec/example_win/default.rb0000644000004100000410000000052013410012544022445 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.8/spec/spec_helper.rb0000644000004100000410000000671213410012544021013 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.8/scenario.yml0000644000004100000410000000043113410012544017561 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.8/.gitignore0000644000004100000410000000032213410012544017222 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.8/LICENSE0000644000004100000410000000206013410012544016240 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.8/Rakefile0000644000004100000410000002167613410012544016716 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.exists?(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.exists?(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.exists?("#{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.exists?(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.8/lib/0000755000004100000410000000000013410012544016003 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/serverspec-runner.rb0000644000004100000410000000004413410012544022016 0ustar www-datawww-datarequire 'serverspec-runner/version' serverspec-runner-1.3.8/lib/extension/0000755000004100000410000000000013410012544020017 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/specinfra/0000755000004100000410000000000013410012544021771 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/specinfra/command/0000755000004100000410000000000013410012544023407 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/specinfra/command/linux/0000755000004100000410000000000013410012544024546 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/specinfra/command/linux/base/0000755000004100000410000000000013410012544025460 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/specinfra/command/linux/base/mysql.rb0000644000004100000410000000111013410012544027143 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.8/lib/extension/specinfra/command/base/0000755000004100000410000000000013410012544024321 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/specinfra/command/base/file.rb0000644000004100000410000000025613410012544025570 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.8/lib/extension/serverspec/0000755000004100000410000000000013410012544022200 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/serverspec/type/0000755000004100000410000000000013410012544023161 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/serverspec/type/mysql.rb0000644000004100000410000000030713410012544024653 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.8/lib/extension/serverspec/type/file.rb0000644000004100000410000000016413410012544024426 0ustar www-datawww-datamodule Serverspec::Type class File < Base def text? @runner.check_file_is_text(@name) end end end serverspec-runner-1.3.8/lib/extension/serverspec/matcher/0000755000004100000410000000000013410012544023623 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/serverspec/matcher/be_replicated.rb0000644000004100000410000000054613410012544026737 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.8/lib/extension/serverspec/helper/0000755000004100000410000000000013410012544023457 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/extension/serverspec/helper/type.rb0000644000004100000410000000055313410012544024770 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.8/lib/serverspec-runner/0000755000004100000410000000000013410012544021473 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/serverspec-runner/version.rb0000644000004100000410000000006013410012544023501 0ustar www-datawww-datamodule ServerspecRunner VERSION = "1.3.8" end serverspec-runner-1.3.8/lib/serverspec-runner/util/0000755000004100000410000000000013410012544022450 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/serverspec-runner/util/hash.rb0000644000004100000410000000053013410012544023716 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.8/lib/serverspec-runner/ansible/0000755000004100000410000000000013410012544023110 5ustar www-datawww-dataserverspec-runner-1.3.8/lib/serverspec-runner/ansible/inventory.rb0000644000004100000410000000301313410012544025467 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.8/Gemfile0000644000004100000410000000014613410012544016531 0ustar www-datawww-datasource "https://rubygems.org" # Specify your gem's dependencies in serverspec-runner.gemspec gemspec serverspec-runner-1.3.8/ssh_options_default.yml0000644000004100000410000000016013410012544022031 0ustar www-datawww-data:port: 22 :paranoid: false #:user: "youre_name" #:keys: ["/path/to/private_key"] #:passphrase: "yourpassphrase" serverspec-runner-1.3.8/serverspec-runner.gemspec0000644000004100000410000000202113410012544022265 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_runtime_dependency 'psych' spec.add_development_dependency "rake" spec.add_development_dependency "rspec-core" spec.add_development_dependency "bundler", "~> 1.3" end