chefspec 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/chefspec.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'chef'
2
+ require 'chefspec/chef_runner'
3
+ require 'chefspec/matchers/execute'
4
+ require 'chefspec/matchers/file'
5
+ require 'chefspec/matchers/log'
6
+ require 'chefspec/matchers/package'
7
+ require 'chefspec/matchers/service'
8
+ require 'chefspec/matchers/shared'
9
+ require 'chefspec/monkey_patches/hash'
@@ -0,0 +1,95 @@
1
+ require 'chef'
2
+ require 'chef/client'
3
+ require 'chef/cookbook_loader'
4
+ require 'chefspec/matchers/shared'
5
+
6
+ # ChefSpec allows you to write rspec examples for Chef recipes to gain faster feedback without the need to converge a
7
+ # node.
8
+ module ChefSpec
9
+
10
+ # The main entry point for running recipes within RSpec.
11
+ class ChefRunner
12
+
13
+ @step_into = []
14
+ @resources = []
15
+
16
+ attr_reader :resources
17
+ attr_reader :node
18
+
19
+ # Instantiate a new runner to run examples with.
20
+ #
21
+ # @param [string] cookbook_path The path to the chef cookbook(s) to be tested
22
+ def initialize(cookbook_path=default_cookbook_path)
23
+ the_runner = self
24
+
25
+ Chef::Resource.class_eval do
26
+ alias :old_run_action :run_action
27
+
28
+ @@runner = the_runner
29
+
30
+ def run_action(action)
31
+ @@runner.resources << self
32
+ end
33
+ end
34
+
35
+ Chef::Config[:solo] = true
36
+ Chef::Config[:cookbook_path] = cookbook_path
37
+ Chef::Log.verbose = true
38
+ Chef::Log.level(:debug)
39
+ @client = Chef::Client.new
40
+ @client.run_ohai
41
+ @node = @client.build_node
42
+ end
43
+
44
+ # Infer the default cookbook path from the location of the calling spec.
45
+ #
46
+ # @return [String] The path to the cookbooks directory
47
+ def default_cookbook_path
48
+ File.join(caller(2).first.split(':').slice(0..-3).to_s, "..", "..", "..")
49
+ end
50
+
51
+ # Run the specified recipes, but without actually converging the node.
52
+ #
53
+ # @param [array] recipe_names The names of the recipes to execute
54
+ def converge(*recipe_names)
55
+ recipe_names.each do |recipe_name|
56
+ @client.node.run_list << recipe_name
57
+ end
58
+
59
+ @resources = []
60
+ run_context = Chef::RunContext.new(@client.node, Chef::CookbookCollection.new(Chef::CookbookLoader.new))
61
+
62
+ runner = Chef::Runner.new(run_context)
63
+ runner.converge
64
+ end
65
+
66
+ # Find any directory declared with the given path
67
+ #
68
+ # @param [String] path The directory path
69
+ # @return [Chef::Resource::Directory] The matching directory, or Nil
70
+ def directory(path)
71
+ find_resource('directory', path)
72
+ end
73
+
74
+ # Find any file declared with the given path
75
+ #
76
+ # @param [String] path The file path
77
+ # @return [Chef::Resource::Directory] The matching file, or Nil
78
+ def file(path)
79
+ find_resource('file', path)
80
+ end
81
+
82
+ private
83
+
84
+ # Find the resource with the declared type and name
85
+ #
86
+ # @param [String] type The type of resource - e.g. 'file' or 'directory'
87
+ # @param [String] name The resource name
88
+ # @return [Chef::Resource] The matching resource, or Nil
89
+ def find_resource(type, name)
90
+ resources.find{|resource| resource_type(resource) == type and resource.name == name}
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,13 @@
1
+ require 'chefspec/matchers/shared'
2
+
3
+ module ChefSpec
4
+ module Matchers
5
+ RSpec::Matchers.define :execute_command do |command|
6
+ match do |chef_run|
7
+ chef_run.resources.any? do |resource|
8
+ resource_type(resource) == 'execute' and resource.command == command
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'chefspec/matchers/shared'
2
+
3
+ module ChefSpec
4
+ module Matchers
5
+
6
+ define_resource_matchers([:create, :delete], [:file, :directory], :path)
7
+
8
+ RSpec::Matchers.define :be_owned_by do |user, group|
9
+ match do |file|
10
+ file.owner == user and file.group == group
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,13 @@
1
+ require 'chefspec/matchers/shared'
2
+
3
+ module ChefSpec
4
+ module Matchers
5
+ RSpec::Matchers.define :log do |message|
6
+ match do |chef_run|
7
+ chef_run.resources.any? do |resource|
8
+ resource_type(resource) == 'log' and resource.name == message
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'chefspec/matchers/shared'
2
+
3
+ module ChefSpec
4
+ module Matchers
5
+
6
+ define_resource_matchers([:install, :remove, :upgrade, :purge], [:package], :package_name)
7
+
8
+ RSpec::Matchers.define :install_package_at_version do |package_name, version|
9
+ match do |chef_run|
10
+ chef_run.resources.any? do |resource|
11
+ resource_type(resource) == 'package' and resource.package_name == package_name and resource.action.to_s == 'install' and resource.version == version
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'chefspec/matchers/shared'
2
+
3
+ module ChefSpec
4
+ module Matchers
5
+
6
+ define_resource_matchers([:start, :stop, :restart, :reload], [:service], :service_name)
7
+
8
+ RSpec::Matchers.define :set_service_to_start_on_boot do |service|
9
+ match do |chef_run|
10
+ chef_run.resources.any? do |resource|
11
+ resource_type(resource) == 'service' and resource.service_name == service and resource.action.include? :enable
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ # Given a resource return the unqualified type it is
2
+ #
3
+ # @param [String] resource A Chef Resource
4
+ # @return [String] The resource type
5
+ def resource_type(resource)
6
+ resource.class.name.split("::").last.downcase
7
+ end
8
+
9
+ # Define simple RSpec matchers for the product of resource types and actions
10
+ #
11
+ # @param [Array] actions The valid actions - for example [:create, :delete]
12
+ # @param [Array] resource_types The resource types
13
+ # @param [Symbol] name_attribute The name attribute of the resource types
14
+ def define_resource_matchers(actions, resource_types, name_attribute)
15
+ actions.product(resource_types).flatten.each_slice(2) do |action, resource_type|
16
+ RSpec::Matchers.define "#{action}_#{resource_type}".to_sym do |name|
17
+ match do |chef_run|
18
+ accepted_types = [resource_type.to_s]
19
+ accepted_types << 'template' if action.to_s == 'create' and resource_type.to_s == 'file'
20
+ chef_run.resources.any? do |resource|
21
+ accepted_types.include? resource_type(resource) and resource.send(name_attribute) == name and
22
+ resource.action.to_s.include? action.to_s
23
+ end
24
+ end
25
+ failure_message_for_should do |actual|
26
+ "No #{resource_type} resource named '#{name}' with action :#{action} found."
27
+ end
28
+ failure_message_for_should_not do |actual|
29
+ "Found #{resource_type} resource named '#{name}' with action :#{action} that should not exist."
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ # Ruby stdlib Hash class
2
+ class Hash
3
+
4
+ # Monkey-patch to stdlib Hash to give us auto-vivifying hashes like Chef.
5
+ # @param [Symbol] method_id The method name
6
+ def method_missing(method_id)
7
+ key = method_id.id2name
8
+ if has_key?(key)
9
+ self[key]
10
+ elsif has_key?(key.to_sym)
11
+ self[key.to_sym]
12
+ else
13
+ super.method_missing(method_id)
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chefspec
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Andrew Crump
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-31 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: Write RSpec examples for Opscode Chef recipes
22
+ email:
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/chefspec/chef_runner.rb
31
+ - lib/chefspec/matchers/execute.rb
32
+ - lib/chefspec/matchers/file.rb
33
+ - lib/chefspec/matchers/log.rb
34
+ - lib/chefspec/matchers/package.rb
35
+ - lib/chefspec/matchers/service.rb
36
+ - lib/chefspec/matchers/shared.rb
37
+ - lib/chefspec/monkey_patches/hash.rb
38
+ - lib/chefspec.rb
39
+ homepage: http://acrmp.github.com/chefspec
40
+ licenses:
41
+ - MIT
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ hash: 3
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.6
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: chefspec-0.0.1
72
+ test_files: []
73
+