chefspec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+