foodtaster 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in foodtaster.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mike Lapshin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Foodtaster
2
+
3
+ Foodtaster is a library for testing your Chef code with RSpec. Specs
4
+ are actually executed on VirtualBox machine(s) managed by
5
+ [Vagrant](http://www.vagrantup.com/).
6
+
7
+ Foodtaster uses VM snapshots to bring something like DB transactions
8
+ into your cookbook specs. Before each Chef Run VM is rolled-back into
9
+ initial 'clean' state which removes any modifications made by
10
+ previously executed specs. It allows you to independently test different
11
+ cookbooks on a single VM.
12
+
13
+ Of course, you aren't limited by just one VM for your specs, you may
14
+ run as many as you need. PostgreSQL replication, load balancing and
15
+ even entire application environments becomes testable (of course, if
16
+ you have enought amount of RAM).
17
+
18
+ Foodtaster is on early development stage, so feedback is very
19
+ appreciated.
20
+
21
+ ## Quick Example
22
+
23
+ ```ruby
24
+ require 'spec_helper'
25
+
26
+ describe "nginx::default" do
27
+ run_chef_on :vm0 do |c|
28
+ c.json = {}
29
+ c.add_recipe 'nginx'
30
+ end
31
+
32
+ it "should install nginx as a daemon" do
33
+ vm0.should have_package 'nginx'
34
+ vm0.should have_user('www-data').in_group('www-data')
35
+ vm0.should listen_port(80)
36
+ vm0.should open_page("http://localhost/")
37
+
38
+ vm0.should have_file("/etc/init.d/nginx")
39
+ vm0.should have_file("/etc/nginx/nginx.conf").with_content(/gzip on/)
40
+ end
41
+
42
+ it "should have valid nginx config" do
43
+ result = vm0.execute("nginx -t")
44
+
45
+ result.should be_successfull
46
+ result.stdout.should include("/etc/nginx/nginx.conf syntax is ok")
47
+ end
48
+ end
49
+ ```
50
+
51
+ ## Installation
52
+
53
+ First, install Vagrant for your system following [official
54
+ instructions](http://docs.vagrantup.com/v2/installation/index.html).
55
+ Then, install two plugins: `sahara` and `vagrant-foodtaster-server`:
56
+
57
+ vagrant plugin install sahara
58
+ vagrant plugin install vagrant-foodtaster-server
59
+
60
+ That's all, you are ready to go.
61
+
62
+ ## Usage
63
+
64
+ In your Chef repository, create a basic Gemfile:
65
+
66
+ source 'https://rubygems.org/'
67
+
68
+ gem 'rspec'
69
+ gem 'foodtaster'
70
+
71
+ Then, create a Vagrantfile describing VMs you need for specs. Here is
72
+ [example
73
+ Vagrantfile](http://raw.github.com/mlapshin/foodtaster-example/master/Vagrantfile).
74
+
75
+ Create `spec` folder with `spec_helper.rb` file:
76
+
77
+ ```ruby
78
+ require 'foodtaster'
79
+
80
+ RSpec.configure do |config|
81
+ config.color_enabled = true
82
+ end
83
+
84
+ Foodtaster.configure do |config|
85
+ config.log_level = :info
86
+ end
87
+ ```
88
+
89
+ You are ready to write cookbook specs. Run them as usual with command:
90
+
91
+ bundle exec rspec spec
92
+
93
+ ## Contributing
94
+
95
+ 1. Fork it
96
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
97
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
98
+ 4. Push to the branch (`git push origin my-new-feature`)
99
+ 5. Create new Pull Request
100
+
101
+ ## License
102
+
103
+ Foodtaster is distributed under [MIT
104
+ License](http://raw.github.com/mlapshin/foodtaster/master/LICENSE).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'foodtaster/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "foodtaster"
8
+ gem.version = Foodtaster::VERSION
9
+ gem.authors = ["Mike Lapshin"]
10
+ gem.email = ["mikhail.a.lapshin@gmail.com"]
11
+ gem.description = %q{RSpec for Chef cookbooks run on Vagrant}
12
+ gem.summary = %q{Foodtaster is a library for testing your Chef code with RSpec.}
13
+ gem.homepage = "http://github.com/mlapshin/foodtaster"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency('rspec', '>= 2.10.0')
21
+ end
@@ -0,0 +1,30 @@
1
+ require 'drb'
2
+
3
+ module Foodtaster
4
+ class Client
5
+ def initialize(drb_port)
6
+ # start local service to be able to redirect stdout & stderr
7
+ # to client
8
+ DRb.start_service("druby://localhost:0")
9
+ @v = DRbObject.new_with_uri("druby://localhost:#{drb_port}")
10
+
11
+ init
12
+ end
13
+
14
+ [:vm_defined?, :prepare_vm, :rollback_vm,
15
+ :run_chef_on_vm, :execute_command_on_vm].each do |method_name|
16
+ define_method method_name do |*args|
17
+ @v.send(method_name, *args)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def init
24
+ $stdout.extend DRbUndumped
25
+ $stderr.extend DRbUndumped
26
+
27
+ @v.redirect_stdstreams($stdout, $stderr)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module Foodtaster
2
+ class Config
3
+ %w(log_level drb_port vagrant_binary).each do |attr|
4
+ attr_accessor attr.to_sym
5
+ end
6
+
7
+ def initialize
8
+ @log_level = :info
9
+ @drb_port = 35672
10
+ @vagrant_binary = 'vagrant'
11
+ end
12
+
13
+ def self.default
14
+ self.new
15
+ end
16
+ end
17
+
18
+ class << self
19
+ def config
20
+ @config ||= Config.default
21
+ end
22
+
23
+ def configure
24
+ if block_given?
25
+ yield(self.config)
26
+ else
27
+ raise ArgumentError, "No block given"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ RSpec::configure do |config|
2
+ config.include Foodtaster::RSpec::ExampleMethods
3
+ config.extend Foodtaster::RSpec::DslMethods
4
+
5
+ config.before(:suite) do
6
+ Foodtaster::RSpecRun.current.start
7
+ end
8
+
9
+ config.after(:suite) do
10
+ Foodtaster::RSpecRun.current.stop
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ module Foodtaster
2
+ module RSpec
3
+ module DslMethods
4
+ def run_chef_on(vm_name, &block)
5
+ Foodtaster::RSpecRun.current.require_vm(vm_name)
6
+
7
+ skip_rollback = true
8
+
9
+ before(:all) do
10
+ vm = get_vm(vm_name)
11
+ vm.rollback unless skip_rollback
12
+ run_chef_on(vm_name, &block)
13
+ end
14
+
15
+ let(vm_name) { get_vm(vm_name) }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ module Foodtaster
2
+ module RSpec
3
+ module ExampleMethods
4
+ def get_vm(vm_name)
5
+ Foodtaster::RSpecRun.current.get_vm(vm_name)
6
+ end
7
+
8
+ def run_chef_on(vm_name, &block)
9
+ chef_config = ChefConfig.new.tap{ |conf| block.call(conf) }.to_hash
10
+ vm = get_vm(vm_name)
11
+ vm.run_chef(chef_config)
12
+ end
13
+
14
+ private
15
+
16
+ class ChefConfig
17
+ attr_accessor :json, :run_list
18
+
19
+ def initialize
20
+ @json = {}
21
+ @run_list = []
22
+ end
23
+
24
+ def add_recipe(name)
25
+ name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/
26
+ run_list << name
27
+ end
28
+
29
+ def add_role(name)
30
+ name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/
31
+ run_list << name
32
+ end
33
+
34
+ def to_hash
35
+ { json: json, run_list: run_list }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ module Foodtaster
2
+ module RSpec
3
+ module Matchers
4
+ class FileMatcher
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def matches?(vm)
10
+ @vm = vm
11
+ @results = {}
12
+ return false unless vm.execute("sudo test -e #{@path}").successful?
13
+
14
+
15
+ if @content
16
+ @actual_content = vm.execute("sudo cat #{@path}").stdout
17
+
18
+ if @content.is_a?(Regexp)
19
+ @results[:content] = !!@actual_content.match(@content)
20
+ else
21
+ @results[:content] = (@actual_content.to_s == @content.to_s)
22
+ end
23
+ end
24
+
25
+ if @owner
26
+ @actual_owner = vm.execute("sudo stat #{@path} -c \"%U\"").stdout.chomp
27
+
28
+ @results[:owner] = (@actual_owner.to_s == @owner.to_s)
29
+ end
30
+
31
+ @results.values.all?
32
+ end
33
+
34
+ def with_content(content)
35
+ @content = content
36
+
37
+ self
38
+ end
39
+
40
+ def with_owner(owner)
41
+ @owner = owner
42
+
43
+ self
44
+ end
45
+
46
+ def failure_message_for_should
47
+ ["expected that #{@vm.name} should have file '#{@path}'",
48
+ @content && !@results[:content] && "with content #{@content.inspect}, but actual content is:\n#{@actual_content.inspect}\n",
49
+ @owner && !@results[:owner] && "with owner #{@owner}, but actual owner is #{@actual_owner}"].delete_if { |a| !a }.join(" ")
50
+ end
51
+
52
+ def failure_message_for_should_not
53
+ "expected that #{@vm.name} should not have file '#{@path}'"
54
+ end
55
+
56
+ def description
57
+ ["have file '#{@path}'",
58
+ @content && "with content #{@content.inspect}",
59
+ @owner && "with owner #{@owner}"].delete_if { |a| !a }.join(" ")
60
+ end
61
+ end
62
+
63
+ module MatcherMethods
64
+ def have_file(path)
65
+ FileMatcher.new(path)
66
+ end
67
+
68
+ alias_method :have_directory, :have_file
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,120 @@
1
+ require 'timeout'
2
+
3
+ # RSpec::Matchers.send(:include, VagrantHelper::Matchers::MatcherMethods)
4
+
5
+ RSpec::Matchers.define :have_running_process do |process|
6
+ match do |vm|
7
+ vm.execute("pgrep #{process}").successful?
8
+ end
9
+
10
+ failure_message_for_should do |vm|
11
+ "expected that #{vm.name} should have running process '#{process}'"
12
+ end
13
+
14
+ failure_message_for_should_not do |vm|
15
+ "expected that #{vm.name} should not have running process '#{process}'"
16
+ end
17
+
18
+ description do
19
+ "have running process '#{process}'"
20
+ end
21
+ end
22
+
23
+ RSpec::Matchers.define :have_package do |package|
24
+ match do |vm|
25
+ vm.execute("dpkg --status #{package}").successful?
26
+ end
27
+
28
+ failure_message_for_should do |vm|
29
+ "expected that #{vm.name} should have installed package '#{package}'"
30
+ end
31
+
32
+ failure_message_for_should_not do |vm|
33
+ "expected that #{vm.name} should not have installed package '#{package}'"
34
+ end
35
+
36
+ description do
37
+ "have installed package '#{package}'"
38
+ end
39
+ end
40
+
41
+ # TODO: I'm not sure if lsof is installed by default
42
+ RSpec::Matchers.define :listen_port do |port|
43
+ match do |vm|
44
+ ->{ vm.execute("sudo lsof -i :#{port.to_s} > /dev/null") }.should be_successful
45
+ end
46
+
47
+ failure_message_for_should do |vm|
48
+ "expected that #{vm.name} should listen port '#{port}'"
49
+ end
50
+
51
+ failure_message_for_should_not do |vm|
52
+ "expected that #{vm.name} should not listen port '#{port}'"
53
+ end
54
+
55
+ description do
56
+ "listen port '#{port}'"
57
+ end
58
+ end
59
+
60
+ RSpec::Matchers.define :have_group do |group|
61
+ match do |vm|
62
+ vm.execute("cat /etc/group | cut -d: -f1 | grep \"\\<#{group}\\>\"").successful?
63
+ end
64
+
65
+ failure_message_for_should do |vm|
66
+ "expected that #{vm.name} should have group '#{group}'"
67
+ end
68
+
69
+ failure_message_for_should_not do |vm|
70
+ "expected that #{vm.name} should not have group '#{group}'"
71
+ end
72
+
73
+ description do
74
+ "have group '#{group}'"
75
+ end
76
+ end
77
+
78
+ RSpec::Matchers.define :open_page do |address|
79
+ match do |vm|
80
+ result = vm.execute("wget #{address} -O /tmp/test-page").successful?
81
+ vm.execute("rm /tmp/test-page")
82
+ result
83
+ end
84
+
85
+ failure_message_for_should do |vm|
86
+ "expected that #{vm.name} should open page '#{address}'"
87
+ end
88
+
89
+ failure_message_for_should_not do |vm|
90
+ "expected that #{vm.name} should not open page '#{address}'"
91
+ end
92
+
93
+ description do
94
+ "open page '#{address}'"
95
+ end
96
+ end
97
+
98
+ def wait_until(_timeout = 5)
99
+ begin
100
+ timeout _timeout do
101
+ until (result = yield)
102
+ sleep 0.5
103
+ end
104
+ result
105
+ end
106
+ rescue Timeout::Error
107
+ nil
108
+ end
109
+ end
110
+
111
+ RSpec::Matchers.define :be_successful do |opts = {}|
112
+ match do |command|
113
+ if command.respond_to?(:call)
114
+ wait_until(opts[:timeout] || 5) { command.call.successful? }
115
+ else
116
+ command.successful?
117
+ end
118
+ end
119
+ end
120
+
@@ -0,0 +1,63 @@
1
+ module Foodtaster
2
+ module RSpec
3
+ module Matchers
4
+ class UserMatcher
5
+ def initialize(username)
6
+ @username = username
7
+ end
8
+
9
+ def matches?(vm)
10
+ @vm = vm
11
+ @results = {}
12
+
13
+ unless vm.execute("cat /etc/passwd | cut -d: -f1 | grep \"\\<#{@username}\\>\"").successful?
14
+ @results[:user] = false
15
+ return false
16
+ end
17
+
18
+ if @group
19
+ @actual_groups = vm.execute("groups #{@username}").stdout.to_s.chomp.split(" ")[2..-1] || []
20
+ @results[:group] = !!@actual_groups.include?(@group)
21
+ end
22
+
23
+ @results.values.all?
24
+ end
25
+
26
+ def in_group(group)
27
+ @group = group
28
+
29
+ self
30
+ end
31
+
32
+ def failure_message_for_should
33
+ msg = ["expected that #{@vm.name} should have user '#{@username}'"]
34
+
35
+ if @group
36
+ msg << "in group #{@group.inspect}"
37
+
38
+ if @results.key?(:group) && !@results[:group]
39
+ msg << " but actual user groups are:\n#{@actual_groups.join(", ")}\n"
40
+ end
41
+ end
42
+
43
+ msg.join(" ")
44
+ end
45
+
46
+ def failure_message_for_should_not
47
+ "expected that #{@vm.name} should not have user '#{@username}'"
48
+ end
49
+
50
+ def description
51
+ ["have user '#{@username}'",
52
+ @group && "in group #{@group}"].delete_if { |a| !a }.join(" ")
53
+ end
54
+ end
55
+
56
+ module MatcherMethods
57
+ def have_user(username)
58
+ UserMatcher.new(username)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ module Foodtaster
2
+ module RSpec
3
+ autoload :ExampleMethods, "foodtaster/rspec/example_methods"
4
+ autoload :DslMethods, "foodtaster/rspec/dsl_methods"
5
+ end
6
+ end
7
+
8
+ require 'foodtaster/rspec/config'
9
+
10
+ # require all matchers
11
+ Dir[File.dirname(__FILE__) + "/rspec/matchers/*.rb"].each do |f|
12
+ require f
13
+ end
14
+
15
+ RSpec::Matchers.send(:include, Foodtaster::RSpec::Matchers::MatcherMethods)
@@ -0,0 +1,97 @@
1
+ require 'set'
2
+
3
+ module Foodtaster
4
+ class RSpecRun
5
+ def initialize
6
+ @required_vm_names = Set.new
7
+ @client = nil
8
+ @server_pid = nil
9
+ end
10
+
11
+ def require_vm(vm_name)
12
+ @required_vm_names.add(vm_name.to_sym)
13
+ end
14
+
15
+ def required_vm_names
16
+ @required_vm_names
17
+ end
18
+
19
+ def get_vm(vm_name)
20
+ Foodtaster::Vm.new(vm_name, @client)
21
+ end
22
+
23
+ def start
24
+ at_exit { self.stop }
25
+
26
+ Foodtaster.logger.debug "Starting Foodtaster specs run"
27
+ start_server_and_connect_client
28
+ prepare_required_vms
29
+ end
30
+
31
+ def stop
32
+ puts "" # newline after rspec output
33
+ terminate_server
34
+ end
35
+
36
+ def client
37
+ @client
38
+ end
39
+
40
+ class << self
41
+ @instance = nil
42
+
43
+ def current
44
+ @instance ||= self.new
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def prepare_required_vms
51
+ self.required_vm_names.each { |vm_name| get_vm(vm_name).prepare }
52
+ end
53
+
54
+ def start_server_and_connect_client(drb_port = Foodtaster.config.drb_port)
55
+ vagrant_binary = Foodtaster.config.vagrant_binary
56
+ vagrant_server_cmd = "#{vagrant_binary} foodtaster-server #{drb_port.to_s} &> /tmp/vagrant-foodtaster-server-output.txt"
57
+
58
+ @server_pid = Process.spawn(vagrant_server_cmd, pgroup: true)
59
+ Foodtaster.logger.debug "Started foodtaster-server on port #{drb_port} with PID #{@server_pid}"
60
+
61
+ connect_client(drb_port)
62
+ end
63
+
64
+ def connect_client(drb_port)
65
+ retry_count = 0
66
+ begin
67
+ sleep 0.2
68
+ @client = Foodtaster::Client.new(drb_port)
69
+ rescue DRb::DRbConnError => e
70
+ Foodtaster.logger.debug "DRb connection failed: #{e.message}"
71
+ retry_count += 1
72
+ retry if retry_count < 10
73
+ end
74
+
75
+ if @client.nil?
76
+ server_output = File.read("/tmp/vagrant-foodtaster-server-output.txt")
77
+
78
+ Foodtaster.logger.fatal "Cannot start or connect to Foodtaster DRb server."
79
+ Foodtaster.logger.fatal "Server output:\n#{server_output}\n"
80
+
81
+ exit 1
82
+ else
83
+ Foodtaster.logger.debug "DRb connection established"
84
+ end
85
+ end
86
+
87
+ def terminate_server
88
+ pgid = Process.getpgid(@server_pid) rescue 0
89
+
90
+ if pgid > 0
91
+ Process.kill("INT", -pgid)
92
+ Process.wait(-pgid)
93
+ Foodtaster.logger.debug "Terminated foodtaster-server process"
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+ module Foodtaster
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,58 @@
1
+ module Foodtaster
2
+ class Vm
3
+ class ExecResult
4
+ attr_reader :stderr
5
+ attr_reader :stdout
6
+ attr_reader :exit_status
7
+
8
+ def initialize(hash)
9
+ @stderr = hash[:stderr]
10
+ @stdout = hash[:stdout]
11
+ @exit_status = hash[:exit_status]
12
+ end
13
+
14
+ def successful?
15
+ exit_status == 0
16
+ end
17
+ end
18
+
19
+ attr_reader :name
20
+
21
+ def initialize(name, client)
22
+ @name = name
23
+ @client = client
24
+
25
+ unless @client.vm_defined?(name)
26
+ raise ArgumentError, "No machine defined with name #{name}"
27
+ end
28
+ end
29
+
30
+ def prepare
31
+ Foodtaster.logger.info "#{name}: Preparing VM"
32
+ @client.prepare_vm(name)
33
+ end
34
+
35
+ def rollback
36
+ Foodtaster.logger.info "#{name}: Rollbacking VM"
37
+ @client.rollback_vm(name)
38
+ end
39
+
40
+ def execute(command)
41
+ Foodtaster.logger.debug "#{name}: Executing #{command}"
42
+ exec_result_hash = @client.execute_command_on_vm(name, command)
43
+
44
+ Foodtaster.logger.debug "#{name}: Finished with #{exec_result_hash[:exit_status]}"
45
+ Foodtaster.logger.debug "#{name}: STDOUT: #{exec_result_hash[:stdout].chomp}"
46
+ Foodtaster.logger.debug "#{name}: STDERR: #{exec_result_hash[:stderr].chomp}"
47
+
48
+ ExecResult.new(exec_result_hash)
49
+ end
50
+
51
+ def run_chef(config)
52
+ Foodtaster.logger.info "#{name}: Running Chef with Run List #{config[:run_list].join(', ')}"
53
+ Foodtaster.logger.debug "#{name}: with JSON: #{config[:json].inspect}"
54
+ @client.run_chef_on_vm(name, config)
55
+ Foodtaster.logger.debug "#{name}: Chef Run finished"
56
+ end
57
+ end
58
+ end
data/lib/foodtaster.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'foodtaster/config'
2
+ require 'foodtaster/rspec'
3
+
4
+ require 'logger'
5
+
6
+ module Foodtaster
7
+ autoload :Client, 'foodtaster/client'
8
+ autoload :Vm, 'foodtaster/vm'
9
+ autoload :RSpecRun, 'foodtaster/rspec_run'
10
+
11
+ class << self
12
+ def logger
13
+ @logger ||= Logger.new(STDOUT).tap do |log|
14
+ log_level = ENV['FT_LOGLEVEL'] || self.config.log_level.to_s.upcase
15
+ log.level = Logger.const_get(log_level)
16
+
17
+ log.formatter = proc do |severity, datetime, progname, msg|
18
+ "[FT #{severity}]: #{msg}\n"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: foodtaster
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mike Lapshin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.10.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.10.0
30
+ description: RSpec for Chef cookbooks run on Vagrant
31
+ email:
32
+ - mikhail.a.lapshin@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - foodtaster.gemspec
43
+ - lib/foodtaster.rb
44
+ - lib/foodtaster/client.rb
45
+ - lib/foodtaster/config.rb
46
+ - lib/foodtaster/rspec.rb
47
+ - lib/foodtaster/rspec/config.rb
48
+ - lib/foodtaster/rspec/dsl_methods.rb
49
+ - lib/foodtaster/rspec/example_methods.rb
50
+ - lib/foodtaster/rspec/matchers/file_matcher.rb
51
+ - lib/foodtaster/rspec/matchers/simple_matchers.rb
52
+ - lib/foodtaster/rspec/matchers/user_matcher.rb
53
+ - lib/foodtaster/rspec_run.rb
54
+ - lib/foodtaster/version.rb
55
+ - lib/foodtaster/vm.rb
56
+ homepage: http://github.com/mlapshin/foodtaster
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.24
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Foodtaster is a library for testing your Chef code with RSpec.
80
+ test_files: []