diff options
author | Lamont Granquist <lamont@opscode.com> | 2013-03-15 15:17:07 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@opscode.com> | 2013-03-15 16:39:35 -0700 |
commit | b480aca6eb0167928cabfb47291a91ddcb50e434 (patch) | |
tree | b2375ab1e65ed8535ee4dd9a5fb552a5f5285a72 /lib/chef/provider/file | |
parent | 6b653284a81195d13c6561371fdcf70bb0fa0ee7 (diff) | |
download | chef-b480aca6eb0167928cabfb47291a91ddcb50e434.tar.gz |
refactoring of file providers
Diffstat (limited to 'lib/chef/provider/file')
-rw-r--r-- | lib/chef/provider/file/content.rb | 75 | ||||
-rw-r--r-- | lib/chef/provider/file/content/cookbook_file.rb | 50 | ||||
-rw-r--r-- | lib/chef/provider/file/content/file.rb | 48 | ||||
-rw-r--r-- | lib/chef/provider/file/content/remote_file.rb | 112 | ||||
-rw-r--r-- | lib/chef/provider/file/content/template.rb | 58 | ||||
-rw-r--r-- | lib/chef/provider/file/deploy/cp_unix.rb | 52 | ||||
-rw-r--r-- | lib/chef/provider/file/deploy/mv_unix.rb | 60 | ||||
-rw-r--r-- | lib/chef/provider/file/deploy/mv_windows.rb | 51 |
8 files changed, 506 insertions, 0 deletions
diff --git a/lib/chef/provider/file/content.rb b/lib/chef/provider/file/content.rb new file mode 100644 index 0000000000..dd5d98852c --- /dev/null +++ b/lib/chef/provider/file/content.rb @@ -0,0 +1,75 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Provider + class File + class Content + + def initialize(new_resource, current_resource, run_context) + @new_resource = new_resource + @current_resource = current_resource + @run_context = run_context + end + + def run_context + @run_context + end + + def new_resource + @new_resource + end + + def current_resource + @current_resource + end + + def tempfile + @tempfile ||= file_for_provider + end + + private + + # + # Return something that looks like a File or Tempfile and + # you must assume the provider will unlink this file. Copy + # the contents to a Tempfile if you need to. + # + def file_for_provider + raise "class must implement file_for_provider!" + end + + # + # These are important for windows to get permissions right, and may + # be useful for SELinux and other ACL approaches. Please use them + # as the arguments to Tempfile.new() consistently. + # + def tempfile_basename + basename = ::File.basename(@new_resource.name) + basename.insert 0, "." unless Chef::Platform.windows? # dotfile if we're not on windows + basename + end + + def tempfile_dirname + Chef::Config[:file_deployment_uses_destdir] ? ::File.dirname(@new_resource.path) : Dir::tmpdir + end + + end + end + end +end diff --git a/lib/chef/provider/file/content/cookbook_file.rb b/lib/chef/provider/file/content/cookbook_file.rb new file mode 100644 index 0000000000..9071aa4a3a --- /dev/null +++ b/lib/chef/provider/file/content/cookbook_file.rb @@ -0,0 +1,50 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider/file/content' + +class Chef + class Provider + class File + class Content + class CookbookFile < Chef::Provider::File::Content + + private + + def file_for_provider + cookbook = run_context.cookbook_collection[resource_cookbook] + file_cache_location = cookbook.preferred_filename_on_disk_location(run_context.node, :files, @new_resource.source, @new_resource.path) + if file_cache_location.nil? + nil + else + tempfile = Tempfile.open(tempfile_basename, tempfile_dirname) + tempfile.close + Chef::Log.debug("#{@new_resource} staging #{file_cache_location} to #{tempfile.path}") + FileUtils.cp(file_cache_location, tempfile.path) + tempfile + end + end + + def resource_cookbook + @new_resource.cookbook || @new_resource.cookbook_name + end + end + end + end + end +end diff --git a/lib/chef/provider/file/content/file.rb b/lib/chef/provider/file/content/file.rb new file mode 100644 index 0000000000..b0a1234956 --- /dev/null +++ b/lib/chef/provider/file/content/file.rb @@ -0,0 +1,48 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider/file/content' + +class Chef + class Provider + class File + class Content + class File < Chef::Provider::File::Content + def file_for_provider + if @new_resource.content + tempfile = Tempfile.open(tempfile_basename, tempfile_dirname) + tempfile.write(@new_resource.content) + tempfile.close + tempfile + else + nil + end + end + + private + + def tempfile_basename + basename = ::File.basename(@new_resource.name) + basename.insert 0, "." unless Chef::Platform.windows? # dotfile if we're not on windows + basename + end + end + end + end + end +end diff --git a/lib/chef/provider/file/content/remote_file.rb b/lib/chef/provider/file/content/remote_file.rb new file mode 100644 index 0000000000..7e006d5415 --- /dev/null +++ b/lib/chef/provider/file/content/remote_file.rb @@ -0,0 +1,112 @@ +# +# Author:: Jesse Campbell (<hikeit@gmail.com>) +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'rest_client' +require 'uri' +require 'tempfile' +require 'chef/provider/file/content' + +class Chef + class Provider + class File + class Content + class RemoteFile < Chef::Provider::File::Content + + attr_reader :raw_file_source + + private + + def file_for_provider + Chef::Log.debug("#{@new_resource} checking for changes") + + if current_resource_matches_target_checksum? + Chef::Log.debug("#{@new_resource} checksum matches target checksum (#{@new_resource.checksum}) - not updating") + else + sources = @new_resource.source + raw_file, @raw_file_source = try_multiple_sources(sources) + end + raw_file + end + + private + + # Given an array of source uris, iterate through them until one does not fail + def try_multiple_sources(sources) + sources = sources.dup + source = sources.shift + begin + uri = URI.parse(source) + raw_file = grab_file_from_uri(uri) + rescue SocketError, Errno::ECONNREFUSED, Errno::ENOENT, Errno::EACCES, Timeout::Error, Net::HTTPFatalError, Net::FTPError => e + Chef::Log.warn("#{@new_resource} cannot be downloaded from #{source}: #{e.to_s}") + if source = sources.shift + Chef::Log.info("#{@new_resource} trying to download from another mirror") + retry + else + raise e + end + end + if uri.userinfo + uri.password = "********" + end + return raw_file, uri.to_s + end + + # Given a source uri, return a Tempfile, or a File that acts like a Tempfile (close! method) + def grab_file_from_uri(uri) + if_modified_since = @new_resource.last_modified + if_none_match = @new_resource.etag + uri_dup = uri.dup + if uri_dup.userinfo + uri_dup.password = "********" + end + if uri_dup.to_s == @current_resource.source[0] + if_modified_since ||= @current_resource.last_modified + if_none_match ||= @current_resource.etag + end + if URI::HTTP === uri + #HTTP or HTTPS + raw_file, mtime, etag = Chef::Provider::RemoteFile::HTTP.fetch(uri, if_modified_since, if_none_match) + elsif URI::FTP === uri + #FTP + raw_file, mtime = Chef::Provider::RemoteFile::FTP.fetch(uri, @new_resource.ftp_active_mode, if_modified_since) + etag = nil + elsif uri.scheme == "file" + #local/network file + raw_file, mtime = Chef::Provider::RemoteFile::LocalFile.fetch(uri, if_modified_since) + etag = nil + else + raise ArgumentError, "Invalid uri. Only http(s), ftp, and file are currently supported" + end + unless raw_file.nil? + @new_resource.etag etag unless @new_resource.etag + @new_resource.last_modified mtime unless @new_resource.last_modified + end + return raw_file + end + + def current_resource_matches_target_checksum? + @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{Regexp.escape(@new_resource.checksum)}/ + end + + end + end + end + end +end diff --git a/lib/chef/provider/file/content/template.rb b/lib/chef/provider/file/content/template.rb new file mode 100644 index 0000000000..68673e2116 --- /dev/null +++ b/lib/chef/provider/file/content/template.rb @@ -0,0 +1,58 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/template' +require 'chef/provider/file/content' + +class Chef + class Provider + class File + class Content + class Template < Chef::Provider::File::Content + + include Chef::Mixin::Template + + def template_location + @template_file_cache_location ||= begin + template_finder.find(@new_resource.source, :local => @new_resource.local, :cookbook => @new_resource.cookbook) + end + end + + private + + def file_for_provider + context = {} + context.merge!(@new_resource.variables) + context[:node] = @run_context.node + context[:template_finder] = template_finder + file = nil + render_template(IO.read(template_location), context) { |t| file = t } + file + end + + def template_finder + @template_finder ||= begin + TemplateFinder.new(run_context, @new_resource.cookbook_name, @run_context.node) + end + end + end + end + end + end +end + diff --git a/lib/chef/provider/file/deploy/cp_unix.rb b/lib/chef/provider/file/deploy/cp_unix.rb new file mode 100644 index 0000000000..1640c68538 --- /dev/null +++ b/lib/chef/provider/file/deploy/cp_unix.rb @@ -0,0 +1,52 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# PURPOSE: this strategy should be cross-platform and maintain SELinux contexts +# and windows ACL inheritance, but it uses cp and is both slower and is +# not atomic and may result in a corrupted destination file in low +# disk or power outage situations. +# + +class Chef + class Provider + class File + class Deploy + class CpUnix + def create(file) + Chef::Log.debug("touching #{file} to create it") + FileUtils.touch(file) + end + + def deploy(src, dst) + # we are only responsible for content so restore the dst files perms + mode = ::File.stat(dst).mode & 07777 + uid = ::File.stat(dst).uid + gid = ::File.stat(dst).gid + Chef::Log.debug("saved mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} from #{dst}") + Chef::Log.debug("copying temporary file #{src} into place at #{dst}") + FileUtils.cp(src, dst) + ::File.chmod(mode, dst) + ::File.chown(uid, gid, dst) + Chef::Log.debug("restored mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{dst}") + end + end + end + end + end +end diff --git a/lib/chef/provider/file/deploy/mv_unix.rb b/lib/chef/provider/file/deploy/mv_unix.rb new file mode 100644 index 0000000000..5e8e5ce72e --- /dev/null +++ b/lib/chef/provider/file/deploy/mv_unix.rb @@ -0,0 +1,60 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# PURPOSE: this strategy is atomic, does not mutate file modes, and supports selinux +# +# Note the FileUtils.mv does not have a preserve flag, and the preserve behavior of it is different +# on different rubies (1.8.7 vs 1.9.x) so we are explicit about making certain the tempfile metadata +# is not deployed (technically implementing preserve = false ourselves). +# + +class Chef + class Provider + class File + class Deploy + class MvUnix + def create(file) + Chef::Log.debug("touching #{file} to create it") + FileUtils.touch(file) + end + + def deploy(src, dst) + # we are only responsible for content so restore the dst files perms + Chef::Log.debug("reading modes from #{dst} file") + mode = ::File.stat(dst).mode & 07777 + uid = ::File.stat(dst).uid + gid = ::File.stat(dst).gid + Chef::Log.debug("applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}") + ::File.chmod(mode, src) + ::File.chown(uid, gid, src) + Chef::Log.debug("moving temporary file #{src} into place at #{dst}") + FileUtils.mv(src, dst) + + # handle selinux if we need to run restorecon + if Chef::Config[:selinux_enabled] + Chef::Log.debug("selinux is enabled, fixing selinux permissions") + cmd = "#{Chef::Config[:selinux_restorecon_comand]} #{dst}" + shell_out!(cmd) + end + end + end + end + end + end +end diff --git a/lib/chef/provider/file/deploy/mv_windows.rb b/lib/chef/provider/file/deploy/mv_windows.rb new file mode 100644 index 0000000000..2c4d01c6ae --- /dev/null +++ b/lib/chef/provider/file/deploy/mv_windows.rb @@ -0,0 +1,51 @@ +# +# Author:: Lamont Granquist (<lamont@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# PURPOSE: this strategy is atomic and preserves default umasks, but on windows you must +# not be copying from the temp directory, and will not correctly restore +# SELinux contexts. +# + +class Chef + class Provider + class File + class Deploy + class MvWindows + def create(file) + Chef::Log.debug("touching #{file} to create it") + FileUtils.touch(file) + end + + def deploy(src, dst) + if ::File.dirname(src) != ::File.dirname(dst) + # internal warning for now - in a Windows/SElinux/ACLs world its better to write + # a tempfile to your destination directory and then rename it + Chef::Log.debug("WARNING: moving tempfile across different directories -- this may break permissions") + end + + # FIXME: save all the windows perms off the dst + FileUtils.mv(src, dst) + # FIXME: restore all the windows perms onto the dst + + end + end + end + end + end +end |