summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Somers <somers.ben@gmail.com>2013-05-14 11:25:07 -0700
committerBen Somers <somers.ben@gmail.com>2013-05-14 11:25:07 -0700
commit437ca9a3ba08f3b4288303db1d1ed7c53b452ffb (patch)
tree7d66a19c9e2e9e5b0b0f123d0d7ca15c6d865ae7
parentcba4cccb2d12283f8e39a23bc92c8e925f3ec8ee (diff)
downloadchef-437ca9a3ba08f3b4288303db1d1ed7c53b452ffb.tar.gz
Enabling storage of roles in subdirectories
Storing roles in subdirectories is already effectively enabled for Hosted Chef, as the directory structure gets flattened when they are uploaded. But it is not currently possible with Chef-Solo; one has to flatten the directory manually before running Chef-Solo. This patch changes that behavior, and enables Chef-Solo with roles in subdirectories.
-rw-r--r--lib/chef/role.rb15
-rw-r--r--spec/unit/role_spec.rb31
2 files changed, 31 insertions, 15 deletions
diff --git a/lib/chef/role.rb b/lib/chef/role.rb
index 78bbfadb88..a7e3f657a7 100644
--- a/lib/chef/role.rb
+++ b/lib/chef/role.rb
@@ -231,18 +231,19 @@ class Chef
end
# Load a role from disk - prefers to load the JSON, but will happily load
- # the raw rb files as well.
+ # the raw rb files as well. Can search within directories in the role_path.
def self.from_disk(name, force=nil)
- js_file = File.join(Chef::Config[:role_path], "#{name}.json")
- rb_file = File.join(Chef::Config[:role_path], "#{name}.rb")
+ roles_files = Dir.glob(File.join(Chef::Config[:role_path], "**", "**"))
+ js_path = roles_files.detect { |file| file.match /#{name}\.json$/ }
+ rb_path = roles_files.detect { |file| file.match /#{name}\.rb$/ }
- if File.exists?(js_file) || force == "json"
+ if js_path && (File.exists?(js_path) || force == "json")
# from_json returns object.class => json_class in the JSON.
- Chef::JSONCompat.from_json(IO.read(js_file))
- elsif File.exists?(rb_file) || force == "ruby"
+ Chef::JSONCompat.from_json(IO.read(js_path))
+ elsif rb_path && (File.exists?(rb_path) || force == "ruby")
role = Chef::Role.new
role.name(name)
- role.from_file(rb_file)
+ role.from_file(rb_path)
role
else
raise Chef::Exceptions::RoleNotFound, "Role '#{name}' could not be loaded from disk"
diff --git a/spec/unit/role_spec.rb b/spec/unit/role_spec.rb
index 764d586903..77a47ee6fd 100644
--- a/spec/unit/role_spec.rb
+++ b/spec/unit/role_spec.rb
@@ -246,28 +246,43 @@ describe Chef::Role do
describe "when loading from disk" do
it "should return a Chef::Role object from JSON" do
- File.should_receive(:exists?).with(File.join(Chef::Config[:role_path], 'lolcat.json')).exactly(1).times.and_return(true)
- IO.should_receive(:read).with(File.join(Chef::Config[:role_path], 'lolcat.json')).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
+ Dir.should_receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.json"])
+ file_path = File.join(Chef::Config[:role_path], 'memes/lolcat.json')
+ File.should_receive(:exists?).with(file_path).exactly(1).times.and_return(true)
+ IO.should_receive(:read).with(file_path).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
@role.should be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
end
it "should return a Chef::Role object from a Ruby DSL" do
- File.should_receive(:exists?).with(File.join(Chef::Config[:role_path], 'lolcat.json')).exactly(1).times.and_return(false)
- File.should_receive(:exists?).with(File.join(Chef::Config[:role_path], 'lolcat.rb')).exactly(2).times.and_return(true)
- File.should_receive(:readable?).with(File.join(Chef::Config[:role_path], 'lolcat.rb')).exactly(1).times.and_return(true)
+ Dir.should_receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.rb"])
+ js_path = File.join(Chef::Config[:role_path], 'memes/lolcat.rb')
+ rb_path = File.join(Chef::Config[:role_path], 'memes/lolcat.rb')
+ File.should_receive(:exists?).with(rb_path).exactly(2).times.and_return(true)
+ File.should_receive(:readable?).with(rb_path).exactly(1).times.and_return(true)
ROLE_DSL=<<-EOR
name "ceiling_cat"
description "like Aliens, but furry"
EOR
- IO.should_receive(:read).with(File.join(Chef::Config[:role_path], 'lolcat.rb')).and_return(ROLE_DSL)
+ IO.should_receive(:read).with(rb_path).and_return(ROLE_DSL)
+ @role.should be_a_kind_of(Chef::Role)
+ @role.class.from_disk("lolcat")
+ end
+
+ it "should prefer a Chef::Role Object from JSON over one from a Ruby DSL" do
+ Dir.should_receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.json", "#{Chef::Config[:role_path]}/memes/lolcat.rb"])
+ js_path = File.join(Chef::Config[:role_path], 'memes/lolcat.json')
+ rb_path = File.join(Chef::Config[:role_path], 'memes/lolcat.rb')
+ File.should_receive(:exists?).with(js_path).exactly(1).times.and_return(true)
+ File.should_not_receive(:exists?).with(rb_path)
+ IO.should_receive(:read).with(js_path).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
@role.should be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
end
it "should raise an exception if the file does not exist" do
- File.should_receive(:exists?).with(File.join(Chef::Config[:role_path], 'lolcat.json')).exactly(1).times.and_return(false)
- File.should_receive(:exists?).with(File.join(Chef::Config[:role_path], 'lolcat.rb')).exactly(1).times.and_return(false)
+ Dir.should_receive(:glob).and_return(["#{Chef::Config[:role_path]}/meme.rb"])
+ File.should_not_receive(:exists?)
lambda {@role.class.from_disk("lolcat")}.should raise_error(Chef::Exceptions::RoleNotFound)
end
end