From 7655abea85be5731aa872489ee9561c69f7be0be Mon Sep 17 00:00:00 2001 From: Lamont Granquist Date: Wed, 20 Mar 2013 15:13:05 -0700 Subject: locate objects closer to their primary concerns --- lib/chef/provider/cookbook_file.rb | 2 +- lib/chef/provider/cookbook_file/content.rb | 48 ++++++++++ lib/chef/provider/file.rb | 2 +- lib/chef/provider/file/content.rb | 59 +++---------- lib/chef/provider/file/content/cookbook_file.rb | 50 ----------- lib/chef/provider/file/content/file.rb | 40 --------- lib/chef/provider/file/content/remote_file.rb | 112 ------------------------ lib/chef/provider/file/content/template.rb | 58 ------------ lib/chef/provider/file_content_base.rb | 73 +++++++++++++++ lib/chef/provider/remote_file.rb | 2 +- lib/chef/provider/remote_file/content.rb | 110 +++++++++++++++++++++++ lib/chef/provider/template.rb | 2 +- lib/chef/provider/template/content.rb | 56 ++++++++++++ lib/chef/providers.rb | 8 +- spec/unit/mixin/template_spec.rb | 2 +- 15 files changed, 307 insertions(+), 317 deletions(-) create mode 100644 lib/chef/provider/cookbook_file/content.rb delete mode 100644 lib/chef/provider/file/content/cookbook_file.rb delete mode 100644 lib/chef/provider/file/content/file.rb delete mode 100644 lib/chef/provider/file/content/remote_file.rb delete mode 100644 lib/chef/provider/file/content/template.rb create mode 100644 lib/chef/provider/file_content_base.rb create mode 100644 lib/chef/provider/remote_file/content.rb create mode 100644 lib/chef/provider/template/content.rb diff --git a/lib/chef/provider/cookbook_file.rb b/lib/chef/provider/cookbook_file.rb index e80da7593d..fdf90c963c 100644 --- a/lib/chef/provider/cookbook_file.rb +++ b/lib/chef/provider/cookbook_file.rb @@ -23,7 +23,7 @@ class Chef class CookbookFile < Chef::Provider::File def initialize(new_resource, run_context) - @content_class = Chef::Provider::File::Content::CookbookFile + @content_class = Chef::Provider::CookbookFile::Content super end diff --git a/lib/chef/provider/cookbook_file/content.rb b/lib/chef/provider/cookbook_file/content.rb new file mode 100644 index 0000000000..6142db8323 --- /dev/null +++ b/lib/chef/provider/cookbook_file/content.rb @@ -0,0 +1,48 @@ +# +# Author:: Lamont Granquist () +# 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_base' + +class Chef + class Provider + class CookbookFile + class Content < Chef::Provider::FileContentBase + + 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 diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb index db06c8f53b..91280e13b1 100644 --- a/lib/chef/provider/file.rb +++ b/lib/chef/provider/file.rb @@ -47,7 +47,7 @@ class Chef attr_reader :deployment_strategy def initialize(new_resource, run_context) - @content_class ||= Chef::Provider::File::Content::File + @content_class ||= Chef::Provider::File::Content @deployment_strategy = new_resource.deployment_strategy.new() if new_resource.respond_to?(:deployment_strategy) super end diff --git a/lib/chef/provider/file/content.rb b/lib/chef/provider/file/content.rb index dd5d98852c..2f7987e6e1 100644 --- a/lib/chef/provider/file/content.rb +++ b/lib/chef/provider/file/content.rb @@ -16,59 +16,22 @@ # limitations under the License. # +require 'chef/provider/file_content_base' + 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. - # + class Content < Chef::Provider::FileContentBase def file_for_provider - raise "class must implement 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 - - # - # 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 diff --git a/lib/chef/provider/file/content/cookbook_file.rb b/lib/chef/provider/file/content/cookbook_file.rb deleted file mode 100644 index 9071aa4a3a..0000000000 --- a/lib/chef/provider/file/content/cookbook_file.rb +++ /dev/null @@ -1,50 +0,0 @@ -# -# Author:: Lamont Granquist () -# 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 deleted file mode 100644 index 616631317a..0000000000 --- a/lib/chef/provider/file/content/file.rb +++ /dev/null @@ -1,40 +0,0 @@ -# -# Author:: Lamont Granquist () -# 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 - 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 deleted file mode 100644 index 7e006d5415..0000000000 --- a/lib/chef/provider/file/content/remote_file.rb +++ /dev/null @@ -1,112 +0,0 @@ -# -# Author:: Jesse Campbell () -# Author:: Lamont Granquist () -# 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 deleted file mode 100644 index 68673e2116..0000000000 --- a/lib/chef/provider/file/content/template.rb +++ /dev/null @@ -1,58 +0,0 @@ -# -# Author:: Lamont Granquist () -# 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_content_base.rb b/lib/chef/provider/file_content_base.rb new file mode 100644 index 0000000000..10b3c310fe --- /dev/null +++ b/lib/chef/provider/file_content_base.rb @@ -0,0 +1,73 @@ +# +# Author:: Lamont Granquist () +# 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 FileContentBase + + 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 diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb index df95be176b..057f46d3c3 100644 --- a/lib/chef/provider/remote_file.rb +++ b/lib/chef/provider/remote_file.rb @@ -24,7 +24,7 @@ class Chef class RemoteFile < Chef::Provider::File def initialize(new_resource, run_context) - @content_class = Chef::Provider::File::Content::RemoteFile + @content_class = Chef::Provider::RemoteFile::Content super end diff --git a/lib/chef/provider/remote_file/content.rb b/lib/chef/provider/remote_file/content.rb new file mode 100644 index 0000000000..fbd7a6bb24 --- /dev/null +++ b/lib/chef/provider/remote_file/content.rb @@ -0,0 +1,110 @@ +# +# Author:: Jesse Campbell () +# Author:: Lamont Granquist () +# 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_base' + +class Chef + class Provider + class RemoteFile + class Content < Chef::Provider::FileContentBase + + 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 diff --git a/lib/chef/provider/template.rb b/lib/chef/provider/template.rb index eed65bd26a..e1def4d21b 100644 --- a/lib/chef/provider/template.rb +++ b/lib/chef/provider/template.rb @@ -25,7 +25,7 @@ class Chef class Template < Chef::Provider::File def initialize(new_resource, run_context) - @content_class = Chef::Provider::File::Content::Template + @content_class = Chef::Provider::Template::Content super end diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb new file mode 100644 index 0000000000..e03fca20bf --- /dev/null +++ b/lib/chef/provider/template/content.rb @@ -0,0 +1,56 @@ +# +# Author:: Lamont Granquist () +# 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_base' + +class Chef + class Provider + class Template + class Content < Chef::Provider::FileContentBase + + 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 + diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index 500bc727c8..b1ab2dc56a 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -108,11 +108,11 @@ require 'chef/provider/remote_file/local_file' require "chef/provider/lwrp_base" require 'chef/provider/registry_key' +require 'chef/provider/file_content_base' require 'chef/provider/file/content' -require 'chef/provider/file/content/file' -require 'chef/provider/file/content/remote_file' -require 'chef/provider/file/content/cookbook_file' -require 'chef/provider/file/content/template' +require 'chef/provider/remote_file/content' +require 'chef/provider/cookbook_file/content' +require 'chef/provider/template/content' require 'chef/provider/file/deploy/cp_unix' require 'chef/provider/file/deploy/mv_unix' require 'chef/provider/file/deploy/mv_windows' diff --git a/spec/unit/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb index e86adcc9d8..bded4a697c 100644 --- a/spec/unit/mixin/template_spec.rb +++ b/spec/unit/mixin/template_spec.rb @@ -62,7 +62,7 @@ describe Chef::Mixin::Template, "render_template" do @resource.cookbook_name = 'openldap' @current_resource = @resource.dup - @content_provider = Chef::Provider::File::Content::Template.new(@resource, @current_resource, @run_context) + @content_provider = Chef::Provider::Template::Content.new(@resource, @current_resource, @run_context) @template_context = {} @template_context[:node] = @node -- cgit v1.2.1