Skip to content

Instantly share code, notes, and snippets.

@ssimeonov
Last active December 18, 2015 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ssimeonov/5811992 to your computer and use it in GitHub Desktop.
Save ssimeonov/5811992 to your computer and use it in GitHub Desktop.
Generalized Chef LWRP subclassing and inheritance example. Chef::LWRPFactory will even generate ChefSpec matchers for new resources.
#
# Cookbook Name:: rake-chef
# Recipe:: spec
#
# Copyright (C) 2013 Swoop, Inc.
#
include_recipe 'rvm'
# rake subclasses rvm_shell LWRP
rake 'db:seed' do
cwd '/app/root'
runner :foreman
end
class ChefResourceRake
module Implementation
def lwrp_initialize(name, run_context=nil)
# Call parent's constructor Chef::Resource::RvmShell
yield name, run_context
@provider = Chef::Provider::RvmShell
@allowed_actions = [:nothing, :run]
@task = name
@arguments = []
@bundler = true
@trace = true
@runner = nil
end
def task(arg=nil)
set_or_return(:task, arg, :kind_of => [String])
end
def arguments(*args)
set_or_return(:arguments, args.empty? ? nil : args, :kind_of => [Array]).tap do |result|
if (index = result.find_index(nil))
raise "argument ##{index} cannot be nil. (Arguments: #{result.inspect})"
end
end
end
def bundler(arg=nil)
set_or_return(:bundler, arg, :kind_of => [TrueClass, FalseClass])
end
def trace(arg=nil)
set_or_return(:trace, arg, :kind_of => [TrueClass, FalseClass])
end
def runner(arg=nil)
set_or_return(:runner, arg, :kind_of => [String, Symbol, NilClass])
end
def code(arg=nil)
raise 'code attribute of rake resource is read-only' if arg
code_to_run = [
runner ? "#{runner} run" : nil,
bundler ? 'bundle exec' : nil,
arguments.empty? ?
"rake #{task}" :
"rake #{task}[#{arguments.join(',').shellescape}]",
trace ? '--trace' : nil
].compact.join(' ')
super code_to_run
end
end
def self.register
Chef::LWRPFactory.subclass(
type: :resource,
name: :rake,
parent: :rvm_shell,
identity_attr: :task,
default_action: :run,
include: Implementation,
chefspec: {
matcher: :execute_rake,
resource_type: :rake,
attribute: :code
}
)
end
end
def rake(*args, &block)
ChefResourceRake.register
method_missing(:rake, *args, &block)
end
require_relative 'spec_helper'
describe 'rake-chef::spec' do
let (:chef_run) { ChefSpec::ChefRunner.new.converge 'rake-chef::spec' }
specify { expect(chef_run).to execute_rake 'foreman run bundle exec rake db:seed --trace' }
describe 'rake resource' do
subject { ChefResourceRake.register.new('my:task') }
it { should be_kind_of Chef::Resource::LWRPBase }
its(:task) { should == 'my:task' }
its(:arguments) { should == [] }
its(:bundler) { should be_true }
its(:trace) { should be_true }
its(:runner) { should be_nil }
its(:code) { should == 'bundle exec rake my:task --trace'}
it 'does not allow nil arguments' do
expect { subject.arguments 1, nil, 'hi' }.to raise_error /cannot be nil/
end
it 'does not allow setting the command attribute' do
expect { subject.code 'rake -T' }.to raise_error /read-only/
end
context 'with arguments 1 & "hello" and no bundler' do
before do
subject.arguments 1, 'hello there'
subject.bundler false
end
its(:arguments) { should == [1, 'hello there'] }
its(:bundler) { should be_false }
its(:code) { should == 'rake my:task[1,hello\ there] --trace'}
end
context 'with foreman runner and no trace' do
before do
subject.runner :foreman
subject.trace false
end
its(:runner) { should == :foreman }
its(:code) { should == 'foreman run bundle exec rake my:task'}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment