Skip to content

Commit 053cd72

Browse files
committed
Merge branch 'CHEF-799' of git://github.com/danielsdeleo/chef into danielsdeleo/CHEF-799
2 parents 07b0fdf + 68057b4 commit 053cd72

File tree

14 files changed

+290
-60
lines changed

14 files changed

+290
-60
lines changed

Rakefile

+4
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,10 @@ namespace :features do
419419
t.profile = "provider_template"
420420
end
421421

422+
Cucumber::Rake::Task.new(:remote_directory) do |t|
423+
t.profile = "provider_remote_directory"
424+
end
425+
422426
Cucumber::Rake::Task.new(:git) do |t|
423427
t.profile = "provider_git"
424428
end

chef-server-api/app/controllers/cookbooks.rb

+32-10
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ def show_segment
6565
if params[:id]
6666
case params[:segment]
6767
when "templates","files"
68-
serve_segment_preferred(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
68+
if params[:recursive]
69+
serve_directory_preferred(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
70+
else
71+
serve_segment_preferred(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
72+
end
6973
else
7074
serve_segment_file(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
7175
end
@@ -78,20 +82,19 @@ def serve_segment_preferred(cookbook, segment, files)
7882

7983
to_send = nil
8084

81-
preferences = [
82-
"host-#{params[:fqdn]}",
83-
"#{params[:platform]}-#{params[:version]}",
84-
"#{params[:platform]}",
85-
"default"
86-
]
87-
85+
8886
preferences.each do |pref|
8987
unless to_send
90-
to_send = files.detect { |file| Chef::Log.debug("#{pref.inspect} #{file.inspect}"); file[:name] == params[:id] && file[:specificity] == pref }
88+
Chef::Log.debug("Looking for a file with name `#{params[:id]}' and specificity #{pref}")
89+
to_send = files.detect do |file|
90+
Chef::Log.debug("#{pref.inspect} #{file.inspect}")
91+
file[:name] == params[:id] && file[:specificity] == pref
92+
93+
end
9194
end
9295
end
9396

94-
raise NotFound, "Cannot find a suitable #{segment} file!" unless to_send
97+
raise NotFound, "Cannot find a suitable #{segment} file for #{params[:id]}!" unless to_send
9598
current_checksum = to_send[:checksum]
9699
Chef::Log.debug("#{to_send[:name]} Client Checksum: #{params[:checksum]}, Server Checksum: #{current_checksum}")
97100
if current_checksum == params[:checksum]
@@ -108,6 +111,25 @@ def serve_segment_preferred(cookbook, segment, files)
108111
send_file(file_name)
109112
end
110113
end
114+
115+
def serve_directory_preferred(cookbook, segment, files)
116+
preferred_dir_contents = []
117+
preferences.each do |preference|
118+
preferred_dir_contents = files.select { |file| file[:name] =~ /^#{params[:id]}/ && file[:specificity] == preference }
119+
break unless preferred_dir_contents.empty?
120+
end
121+
122+
raise NotFound, "Cannot find a suitable directory for #{params[:id]}" if preferred_dir_contents.empty?
123+
124+
display preferred_dir_contents.map { |file| file[:name].sub(/^#{params[:id]}/, '') }
125+
end
126+
127+
def preferences
128+
["host-#{params[:fqdn]}",
129+
"#{params[:platform]}-#{params[:version]}",
130+
"#{params[:platform]}",
131+
"default"]
132+
end
111133

112134
def serve_segment_file(cookbook, segment, files)
113135
to_send = files.detect { |f| f[:name] == params[:id] }

chef/lib/chef/provider/remote_directory.rb

+58-49
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ class RemoteDirectory < Chef::Provider::Directory
3535

3636
def action_create
3737
super
38-
39-
@remote_file_list = Hash.new
4038
do_recursive
4139
end
4240

@@ -45,81 +43,92 @@ def action_create
4543
def do_recursive
4644
if Chef::Config[:solo]
4745
Chef::Log.debug("Doing a local recursive directory copy for #{@new_resource}")
48-
files_to_transfer = files_for_directory(@new_resource.source)
4946
else
5047
Chef::Log.debug("Doing a remote recursive directory transfer for #{@new_resource}")
51-
r = Chef::REST.new(Chef::Config[:remotefile_url])
52-
files_to_transfer = r.get_rest(generate_url(@new_resource.source, "files", { :recursive => "true" }))
53-
end
54-
48+
end
49+
5550
files_to_transfer.each do |remote_file_source|
5651
fetch_remote_file(remote_file_source)
5752
end
5853
end
5954

55+
def files_to_transfer
56+
file_list = Chef::Config[:solo] ? generate_solo_file_list : generate_client_file_list
57+
Chef::Log.debug("Generated file manifest for #{@new_resource}:\n#{file_list.join("\n")}")
58+
file_list
59+
end
60+
61+
def generate_solo_file_list
62+
# Pulled from chef-server-slice files controller
63+
directory = find_preferred_file(
64+
@new_resource.cookbook_name,
65+
:remote_file,
66+
@new_resource.source,
67+
@node[:fqdn],
68+
@node[:platform],
69+
@node[:platform_version]
70+
)
71+
72+
unless (directory && ::File.directory?(directory))
73+
raise NotFound, "Cannot find a suitable directory"
74+
end
75+
76+
Dir[::File.join(directory, '**', '*')].sort.reverse.select do |file|
77+
file[/^#{directory}\/(.+)$/, 1] unless ::File.directory?(file)
78+
end
79+
end
80+
81+
def generate_client_file_list
82+
r = Chef::REST.new(Chef::Config[:remotefile_url])
83+
r.get_rest(generate_url(@new_resource.source, "files", { :recursive => "true" }))
84+
end
85+
6086
def fetch_remote_file(remote_file_source)
6187
full_path = ::File.join(@new_resource.path, remote_file_source)
62-
full_dir = ::File.dirname(full_path)
6388

64-
if !::File.directory?(full_dir)
65-
create_directory(full_dir)
66-
end
67-
68-
remote_file = Chef::Resource::RemoteFile.new(full_path, nil, @node)
69-
remote_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
70-
remote_file.source(::File.join(@new_resource.source, remote_file_source))
89+
ensure_directory_exists(::File.dirname(full_path))
90+
91+
file_to_fetch = provider_for_remote_file(full_path, remote_file_source)
92+
file_to_fetch.load_current_resource
93+
file_to_fetch.action_create
94+
@new_resource.updated = true if file_to_fetch.new_resource.updated
95+
end
96+
97+
def provider_for_remote_file(path, source)
98+
remote_file = Chef::Resource::RemoteFile.new(path, nil, @node)
99+
remote_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
100+
remote_file.source(::File.join(@new_resource.source, source))
71101
remote_file.mode(@new_resource.files_mode) if @new_resource.files_mode
72102
remote_file.group(@new_resource.files_group) if @new_resource.files_group
73103
remote_file.owner(@new_resource.files_owner) if @new_resource.files_owner
74104
remote_file.backup(@new_resource.files_backup) if @new_resource.files_backup
75105

76-
rf_provider = Chef::Platform.provider_for_node(@node, remote_file)
77-
rf_provider.load_current_resource
78-
rf_provider.action_create
79-
@new_resource.updated = true if rf_provider.new_resource.updated
106+
Chef::Platform.provider_for_node(@node, remote_file)
80107
end
81108

82-
def create_directory(full_dir)
83-
new_dir = Chef::Resource::Directory.new(full_dir, nil, @node)
109+
def ensure_directory_exists(path)
110+
unless ::File.directory?(path)
111+
directory_to_create = provider_for_directory(path)
112+
directory_to_create.load_current_resource
113+
directory_to_create.action_create
114+
@new_resource.updated = true if directory_to_create.new_resource.updated
115+
end
116+
end
117+
118+
def provider_for_directory(path)
119+
new_dir = Chef::Resource::Directory.new(path, nil, @node)
84120
new_dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
85121
new_dir.mode(@new_resource.mode)
86122
new_dir.group(@new_resource.group)
87123
new_dir.owner(@new_resource.owner)
88124
new_dir.recursive(true)
89125

90-
d_provider = Chef::Platform.provider_for_node(@node, new_dir)
91-
d_provider.load_current_resource
92-
d_provider.action_create
93-
@new_resource.updated = true if d_provider.new_resource.updated
126+
Chef::Platform.provider_for_node(@node, new_dir)
94127
end
95128

96129
def action_create_if_missing
97130
raise Chef::Exceptions::UnsupportedAction, "Remote Directories do not support create_if_missing."
98131
end
99-
# Pulled from chef-server-slice files controller
100-
101-
def files_for_directory(path)
102-
directory = find_preferred_file(
103-
@new_resource.cookbook_name,
104-
:remote_file,
105-
path,
106-
@node[:fqdn],
107-
@node[:platform],
108-
@node[:platform_version]
109-
)
110-
111-
unless (directory && ::File.directory?(directory))
112-
raise NotFound, "Cannot find a suitable directory"
113-
end
114-
115-
directory_listing = Array.new
116-
Dir[::File.join(directory, '**', '*')].sort { |a,b| b <=> a }.each do |file|
117-
next if ::File.directory?(file)
118-
file =~ /^#{directory}\/(.+)$/
119-
directory_listing << $1
120-
end
121-
directory_listing
122-
end
123132

124133
end
125134
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I'm a file inside a the root remote_directory source dir.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I'm a file in a subdirectory inside a remote_directory source

chef/spec/unit/platform_spec.rb

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2020

2121
describe Chef::Platform do
22+
before :all do
23+
@original_platform_map = Chef::Platform.platforms
24+
end
25+
26+
after :all do ||
27+
Chef::Platform.platforms = @original_platform_map
28+
end
29+
2230
before(:each) do
2331
Chef::Platform.platforms = {
2432
:darwin => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#
2+
# Author:: Daniel DeLeo (<[email protected]>)
3+
# Copyright:: Copyright (c) 2010 Daniel DeLeo
4+
# License:: Apache License, Version 2.0
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
20+
21+
describe Chef::Provider::RemoteDirectory do
22+
before do
23+
@resource = Chef::Resource::RemoteDirectory.new("/tmp/tafty")
24+
@resource.source "path/on/server"
25+
@node = Chef::Node.new
26+
@node.name "latte"
27+
@node.platform :mac_os_x
28+
@node.platform_version "10.6"
29+
@provider = Chef::Provider::RemoteDirectory.new(@node, @resource)
30+
@provider.current_resource = @resource.clone
31+
end
32+
33+
it "doesn't support create_if_missing and explodes if you try to use it" do
34+
lambda {@provider.send :action_create_if_missing}.should raise_error(Chef::Exceptions::UnsupportedAction)
35+
end
36+
37+
describe "recursively transferring files" do
38+
before do
39+
@resource.mode "0750"
40+
@resource.group "wheel"
41+
@resource.owner "root"
42+
43+
@resource.files_mode "0640"
44+
@resource.files_group "staff"
45+
@resource.files_owner "toor"
46+
@resource.files_backup 23
47+
48+
@provider.current_resource = @resource.clone
49+
end
50+
51+
it "creates Directory resources and assigns them the correct attributes" do
52+
directory_resource = @provider.send(:provider_for_directory, "/tmp/intermediate_dir").new_resource
53+
directory_resource.path.should == "/tmp/intermediate_dir"
54+
directory_resource.mode.should == "0750"
55+
directory_resource.group.should == "wheel"
56+
directory_resource.owner.should == "root"
57+
directory_resource.recursive.should be_true
58+
end
59+
60+
it "creates intermediate directories as required" do
61+
@directory_resource = mock("Resource::Directory", :updated => true)
62+
@directory_provider = mock("Provider::Directory", :new_resource => @directory_resource)
63+
@directory_provider.should_receive(:load_current_resource)
64+
@directory_provider.should_receive(:action_create)
65+
@provider.stub!(:provider_for_directory).and_return(@directory_provider)
66+
@provider.send(:ensure_directory_exists, "/tmp/an_intermediate_dir")
67+
@resource.updated.should be_true
68+
end
69+
70+
it "creates remote_file resources and assigns them the correct attributes" do
71+
@resource.cookbook "berlin_style_tasty_cupcakes"
72+
rf_provider = @provider.send(:provider_for_remote_file,
73+
"/enclosing_dir/file_to_transfer.txt",
74+
"file_to_transfer.txt")
75+
rf_resource = rf_provider.new_resource
76+
rf_resource.cookbook_name.should == "berlin_style_tasty_cupcakes"
77+
rf_resource.source.should == "path/on/server/file_to_transfer.txt"
78+
rf_resource.mode.should == "0640"
79+
rf_resource.group.should == "staff"
80+
rf_resource.owner.should == "toor"
81+
rf_resource.backup.should == 23
82+
end
83+
84+
it "fetches files using remote file resources" do
85+
@rf_resource = mock("Resource::RemoteFile", :updated => true)
86+
@rf_provider = mock("Provider::RemoteFile", :new_resource => @rf_resource)
87+
88+
@rf_provider.should_receive(:load_current_resource)
89+
@rf_provider.should_receive(:action_create)
90+
91+
@provider.stub!(:ensure_directory_exists)
92+
@provider.stub!(:provider_for_remote_file).and_return(@rf_provider)
93+
94+
@provider.send(:fetch_remote_file, "foo")
95+
@resource.updated.should be_true
96+
end
97+
end
98+
99+
describe "generating the list of files to transfer" do
100+
101+
after do
102+
Chef::Config[:solo] = false
103+
end
104+
105+
it "lists the directory contents from the cookbook for chef-solo" do
106+
Chef::Config[:solo] = true
107+
@source_path = File.join(File.dirname(__FILE__), "..", "..", "data", "remote_directory_data")
108+
@resource.source(@source_path)
109+
@provider.stub!(:find_preferred_file).and_return(@source_path)
110+
111+
file_list = @provider.send(:generate_solo_file_list)
112+
file_list.map! { |f| ::File.expand_path(f) }
113+
expected_file_list = %w{ remote_subdirectory/remote_subdir_file.txt remote_dir_file.txt }
114+
expected_file_list.map! { |f| ::File.expand_path(::File.join(@source_path, f)) }
115+
file_list.should == expected_file_list
116+
end
117+
118+
it "requests a recursive file listing from the server for chef-client" do
119+
@resource.source("dir_on_the_server")
120+
Chef::Config[:solo] = false
121+
122+
@rest = mock("Chef::REST")
123+
Chef::REST.should_receive(:new).and_return(@rest)
124+
125+
list = %w{ foo bar baz}
126+
@rest.should_receive(:get_rest).with("cookbooks//files?id=dir_on_the_server&recursive=true").and_return(list)
127+
@provider.send(:generate_client_file_list).should == list
128+
129+
end
130+
131+
end
132+
end

cucumber.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ client_run_interval: --tags client_run_interval --format pretty -r features/step
4545
recipe_inclusion: --tags recipe_inclusion --format pretty -r features/steps -r features/support features
4646
attribute_inclusion: --tags @attribute_inclusion --format pretty -r features/steps -r features/support features
4747
cookbooks: --tags @cookbooks --format pretty -r features/steps -r features/support features
48-
provider_remote_file: --tags provider,remote_file --format pretty -r features/steps -r features/support features
48+
provider_remote_directory: --tags @remote_directory --format pretty -r features/steps -r features/support features
4949
provider_git: --tags provider,git --format pretty -r features/steps -r features/support features
5050
provider_template: --tags template --format pretty -r features/steps -r features/support features
5151
provider_package_macports: --tags macports --format pretty -r features/steps -r features/support features
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Space Manoeuvres stage 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
micromega

0 commit comments

Comments
 (0)