diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d87d4be6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..a43dfbe2 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in serverspec.gemspec +gemspec diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..d56d15f2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2013 Gosuke Miyashita + +MIT License + +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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..4ace0e98 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Serverspec + +RSpec tests for your provisioned servers + +---- + +## Installation + +Add this line to your application's Gemfile: + + gem 'serverspec' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install serverspec + +---- + +## Usage + +``` +$ serverspec-init + + spec/ + + spec/www.example.jp/ + + spec/spec_helper.rb + + Rakefile + + spec/www.example.jp/httpd_spec.rb +``` +spec/www.example.jp/httpd_spec.rb is a sample spec file and its content is like this. + +```ruby +require 'spec_helper' + +describe 'httpd' do + it { should be_installed } + it { should be_enabled } + it { should be_running } +end + +describe 'port 80' do + it { should be_listening } +end + +describe '/etc/httpd/conf/httpd.conf' do + it { should be_file } + it { should contain "ServerName www.example.jp" } +end +``` + +You can write spec for testing provisioned servers like this. + +You should create ~/.ssh/config like this before running tests. + +``` +Host *.example.jp + User root + IdentityFile ~/.ssh/id_rsa +``` + +---- + +## 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 diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..29955274 --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require "bundler/gem_tasks" diff --git a/bin/serverspec-init b/bin/serverspec-init new file mode 100755 index 00000000..f199754f --- /dev/null +++ b/bin/serverspec-init @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) + +require 'serverspec' + +Serverspec::Setup.run diff --git a/lib/serverspec.rb b/lib/serverspec.rb new file mode 100644 index 00000000..f43d2c49 --- /dev/null +++ b/lib/serverspec.rb @@ -0,0 +1,11 @@ +require 'rubygems' +require 'rspec' +require 'serverspec/version' +require 'serverspec/matchers' +require 'serverspec/helper' +require 'serverspec/setup' + +RSpec.configure do |c| + c.include(Serverspec::Helper) + c.add_setting :host, :default => nil +end diff --git a/lib/serverspec/helper.rb b/lib/serverspec/helper.rb new file mode 100644 index 00000000..197f7141 --- /dev/null +++ b/lib/serverspec/helper.rb @@ -0,0 +1,8 @@ +module Serverspec + module Helper + def ssh_exec(host, cmd, opt={}) + `ssh root@#{host} -o'StrictHostKeyChecking no' -o'UserKnownHostsFile /dev/null' #{cmd}` + end + end +end + diff --git a/lib/serverspec/matchers.rb b/lib/serverspec/matchers.rb new file mode 100644 index 00000000..5b5fc635 --- /dev/null +++ b/lib/serverspec/matchers.rb @@ -0,0 +1,6 @@ +require 'serverspec/matchers/be_enabled' +require 'serverspec/matchers/be_file' +require 'serverspec/matchers/be_installed' +require 'serverspec/matchers/be_listening' +require 'serverspec/matchers/be_running' +require 'serverspec/matchers/contain' diff --git a/lib/serverspec/matchers/be_enabled.rb b/lib/serverspec/matchers/be_enabled.rb new file mode 100644 index 00000000..544695ad --- /dev/null +++ b/lib/serverspec/matchers/be_enabled.rb @@ -0,0 +1,6 @@ +RSpec::Matchers.define :be_enabled do + match do |actual| + ssh_exec(RSpec.configuration.host, "chkconfig --list #{actual} 2> /dev/null | grep 3:on") + $? == 0 + end +end diff --git a/lib/serverspec/matchers/be_file.rb b/lib/serverspec/matchers/be_file.rb new file mode 100644 index 00000000..6dbb530f --- /dev/null +++ b/lib/serverspec/matchers/be_file.rb @@ -0,0 +1,6 @@ +RSpec::Matchers.define :be_file do + match do |actual| + ssh_exec(RSpec.configuration.host, "test -f #{actual} 2> /dev/null") + $? == 0 + end +end diff --git a/lib/serverspec/matchers/be_installed.rb b/lib/serverspec/matchers/be_installed.rb new file mode 100644 index 00000000..2a1d51b6 --- /dev/null +++ b/lib/serverspec/matchers/be_installed.rb @@ -0,0 +1,6 @@ +RSpec::Matchers.define :be_installed do + match do |actual| + ssh_exec(RSpec.configuration.host, "rpm -q #{actual} 2> /dev/null") + $? == 0 + end +end diff --git a/lib/serverspec/matchers/be_listening.rb b/lib/serverspec/matchers/be_listening.rb new file mode 100644 index 00000000..3f763d0e --- /dev/null +++ b/lib/serverspec/matchers/be_listening.rb @@ -0,0 +1,7 @@ +RSpec::Matchers.define :be_listening do + match do |actual| + port = actual.gsub(/port\s+/, '') + ssh_exec(RSpec.configuration.host, "netstat -tnl 2> /dev/null | grep ':#{port} '") + $? == 0 + end +end diff --git a/lib/serverspec/matchers/be_running.rb b/lib/serverspec/matchers/be_running.rb new file mode 100644 index 00000000..607b5acf --- /dev/null +++ b/lib/serverspec/matchers/be_running.rb @@ -0,0 +1,6 @@ +RSpec::Matchers.define :be_running do + match do |actual| + ssh_exec(RSpec.configuration.host, "service #{actual} status 2> /dev/null") + $? == 0 + end +end diff --git a/lib/serverspec/matchers/contain.rb b/lib/serverspec/matchers/contain.rb new file mode 100644 index 00000000..3a4f8f9a --- /dev/null +++ b/lib/serverspec/matchers/contain.rb @@ -0,0 +1,6 @@ +RSpec::Matchers.define :contain do |expected| + match do |actual| + ssh_exec(RSpec.configuration.host, "\"grep -q '#{expected}' #{actual} \" 2> /dev/null") + $? == 0 + end +end diff --git a/lib/serverspec/setup.rb b/lib/serverspec/setup.rb new file mode 100644 index 00000000..0fe798b7 --- /dev/null +++ b/lib/serverspec/setup.rb @@ -0,0 +1,102 @@ +require 'fileutils' + +module Serverspec + class Setup + def self.run + %w( spec spec/www.example.jp ).each { |dir| safe_mkdir(dir) } + safe_create_spec_helper + safe_create_rakefile + safe_create_spec + end + + def self.safe_create_spec + content = <<-EOF +require 'spec_helper' + +describe 'httpd' do + it { should be_installed } + it { should be_enabled } + it { should be_running } +end + +describe 'port 80' do + it { should be_listening } +end + +describe '/etc/httpd/conf/httpd.conf' do + it { should be_file } + it { should contain "ServerName www.example.jp" } +end +EOF + + if File.exists? 'spec/www.example.jp/httpd_spec.rb' + old_content = File.read('spec/www.example.jp/httpd_spec.rb') + if old_content != content + $stderr.puts "!! spec/www.example.jp/httpd_spec.rb already exists and differs from template" + end + else + File.open('spec/www.example.jp/httpd_spec.rb', 'w') do |f| + f.puts content + end + puts ' + spec/www.example.jp/httpd_spec.rb' + end + end + + def self.safe_mkdir(dir) + if File.exists? dir + unless File.directory? dir + $stderr.puts "!! #{dir} already exists and is not a directory" + end + else + FileUtils.mkdir dir + puts " + #{dir}/" + end + end + + def self.safe_create_spec_helper + content = <<-EOF +require 'serverspec' +require 'pathname' + +RSpec.configure do |c| + c.before do + c.host = File.basename(Pathname.new(example.metadata[:location]).dirname) + end +end +EOF + if File.exists? 'spec/spec_helper.rb' + old_content = File.read('spec/spec_helper.rb') + if old_content != content + $stderr.puts "!! spec/spec_helper.rb already exists and differs from template" + end + else + File.open('spec/spec_helper.rb', 'w') do |f| + f.puts content + end + puts ' + spec/spec_helper.rb' + end + end + + def self.safe_create_rakefile + content = <<-'EOF' +require 'rake' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) do |t| + t.pattern = 'spec/*/*_spec.rb' +end +EOF + if File.exists? 'Rakefile' + old_content = File.read('Rakefile') + if old_content != content + $stderr.puts "!! Rakefile already exists and differs from template" + end + else + File.open('Rakefile', 'w') do |f| + f.puts content + end + puts ' + Rakefile' + end + end + end +end diff --git a/lib/serverspec/version.rb b/lib/serverspec/version.rb new file mode 100644 index 00000000..4fd4574e --- /dev/null +++ b/lib/serverspec/version.rb @@ -0,0 +1,3 @@ +module Serverspec + VERSION = "0.0.1" +end diff --git a/serverspec.gemspec b/serverspec.gemspec new file mode 100644 index 00000000..837d3e79 --- /dev/null +++ b/serverspec.gemspec @@ -0,0 +1,23 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'serverspec/version' + +Gem::Specification.new do |spec| + spec.name = "serverspec" + spec.version = Serverspec::VERSION + spec.authors = ["Gosuke Miyashita"] + spec.email = ["gosukenator@gmail.com"] + spec.description = %q{RSpec tests for your provisioned servers} + spec.summary = %q{RSpec tests for your provisioned servers} + spec.homepage = "https://github.com/mizzy/serverspec" + 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_development_dependency "bundler", "~> 1.3" + spec.add_development_dependency "rake" +end