On 03/04/10 21:10, Markus Roberts wrote: > (continued) > > diff --git a/lib/puppet/type/file/checksum.rb >> b/lib/puppet/type/file/checksum.rb >> index 5af943e..5b99bef 100755 >> --- a/lib/puppet/type/file/checksum.rb >> +++ b/lib/puppet/type/file/checksum.rb >> @@ -23,4 +23,11 @@ Puppet::Type.type(:file).newparam(:checksum) do >> method = type.to_s + "_file" >> "{#{type}}" + send(method, path).to_s >> end >> + >> + def sum_stream(&block) >> + type = value || :md5 # same comment as above >> + method = type.to_s + "_stream" >> + checksum = send(method, &block) >> + "{#{type}}#{checksum}" >> + end >> end >> diff --git a/lib/puppet/type/file/content.rb >> b/lib/puppet/type/file/content.rb >> index b6c671b..3789900 100755 >> --- a/lib/puppet/type/file/content.rb >> +++ b/lib/puppet/type/file/content.rb >> @@ -1,9 +1,14 @@ >> +require 'net/http' >> +require 'uri' >> + >> require 'puppet/util/checksums' >> +require 'puppet/network/http/api/v1' >> >> module Puppet >> Puppet::Type.type(:file).newproperty(:content) do >> include Puppet::Util::Diff >> include Puppet::Util::Checksums >> + include Puppet::Network::HTTP::API::V1 >> >> desc "Specify the contents of a file as a string. Newlines, tabs, >> and >> spaces can be specified using the escaped syntax (e.g., \\n for >> a >> @@ -68,21 +73,22 @@ module Puppet >> end >> end >> >> - # If content was specified, return that; else try to return the >> source content; >> + # If content was specified, return that; >> # else, return nil. >> def actual_content >> if defined?(@actual_content) and @actual_content >> return @actual_content >> end >> >> - if s = resource.parameter(:source) >> - return s.content >> - end >> - fail "Could not find actual content from checksum" >> + return nil >> + end >> + >> > > :-) So that would be: > > def actual_content > @actual_content > end
I left it like it was so that your code smell automated refactoring would find it :-D > or even > > attr_reader :actual_content Yes, definitely better :-) > >> + def length >> + (actual_content and actual_content.length) || 0 >> end >> >> def content >> - self.should || (s = resource.parameter(:source) and s.content) >> + self.should >> end >> >> # Override this method to provide diffs if asked for. >> @@ -140,9 +146,62 @@ module Puppet >> # We're safe not testing for the 'source' if there's no >> 'should' >> # because we wouldn't have gotten this far if there weren't at >> least >> # one valid value somewhere. >> - @resource.write(actual_content, :content) >> + @resource.write(:content) >> >> return return_event >> end >> + >> + def write(file) >> + return write_content(file) if actual_content >> + return write_source(file) if resource.parameter(:source) >> + self.fail "Writing content that wasn't provided" >> + end >> + >> + def write_content(file) >> + file.print actual_content >> + return resource.parameter(:checksum).sum(actual_content) >> + end >> + >> + def write_source(file) >> + if source = resource.parameter(:source) >> + return write_local_source(file, source) if source.local? >> + return write_remote_source(file, source) >> + end >> + end >> + >> + def write_local_source(file, source) >> + resource.parameter(:checksum).sum_stream do |sum| >> + File.open(source.full_path, "r") do |src| >> + puts "local open" >> > > I assume the puts should be removed? Oops. >> + while chunk = src.read(8192) >> + sum << chunk >> + file.print chunk >> + end >> + end >> + end >> + end >> + >> + def write_remote_source(file, source) >> + request = Puppet::Indirector::Request.new(:file_content, >> :find, source.full_path) >> + checksum = nil >> + Puppet::Network::HttpPool.http_instance(source.server, >> source.port).request_get(indirection2uri(request), {"Accept" => "raw"}) do >> |response| >> + case response.code >> + when "404" >> + return nil >> + when /^2/ >> + checksum = resource.parameter(:checksum).sum_stream do >> |sum| >> + response.read_body do |chunk| >> + sum << chunk >> + file.print chunk >> + end >> > + end >> + else >> + # Raise the http error if we didn't get a 'success' of >> some kind. >> + message = "Error %s on SERVER: %s" % [response.code, >> (response.body||'').empty? ? response.message : response.body] >> + raise Net::HTTPError.new(message, response) >> + end >> + end >> + return checksum >> + end >> end >> end >> > > Hmmm. More thinking than recommending, but what about something like: > > def write_source(file) > resource.parameter(:checksum).sum_stream { |sum| > each_chunk_from(file,resource.parameter(:source) { |chunk| > sum << chunk > file.print chunk > } > } > end > > def each_chunk_from(file,source) > if source.nil? > nil > elsif source.local? > File.open(source.full_path, "r") do |src| > while chunk = src.read(8192) > yield chunk > end > end > else > request = Puppet::Indirector::Request.new(:file_content, > :find, source.full_path) > connection = > Puppet::Network::HttpPool.http_instance(source.server, source.port). > connection.request_get(indirection2uri(request), {"Accept" > => "raw"}) do |response| > case response.code > when "404"; nil > when /^2/; response.read_body { |chunk| yield chunk } > else > # Raise the http error if we didn't get a 'success' > of some kind. > message = "Error %s on SERVER: %s" % [response.code, > (response.body||'').empty? ? response.message : response.body] > raise Net::HTTPError.new(message, response) > end > } > end > end > > In other words, rather than pushing the checksuming and writing into the > walk the stream code, keep it at the write_source level and push the > local/remote choice down instead? If this direction is good, it looks like > we could go even further and unify write_source and write_content, by adding > the content logic at the head of each_chunk_from. Yes, that should work fine, but you'll have to wait next week-end for this as I'll be AFK most of this week. > >> diff --git a/lib/puppet/type/file/ensure.rb >> b/lib/puppet/type/file/ensure.rb >> index 5f0beee..b8a85f6 100755 >> --- a/lib/puppet/type/file/ensure.rb >> +++ b/lib/puppet/type/file/ensure.rb >> @@ -45,7 +45,7 @@ module Puppet >> if property = @resource.property(:content) >> property.sync >> else >> - @resource.write("", :ensure) >> + @resource.write(:ensure) >> mode = @resource.should(:mode) >> end >> return :file_created >> diff --git a/lib/puppet/type/file/source.rb >> b/lib/puppet/type/file/source.rb >> index 838dabb..5b86e82 100755 >> --- a/lib/puppet/type/file/source.rb >> +++ b/lib/puppet/type/file/source.rb >> @@ -96,16 +96,6 @@ module Puppet >> metadata && metadata.checksum >> end >> >> - # Look up (if necessary) and return remote content. >> - cached_attr(:content) do >> - raise Puppet::DevError, "No source for content was stored with >> the metadata" unless metadata.source >> - >> - unless tmp = >> Puppet::FileServing::Content.find(metadata.source) >> - fail "Could not find any content at %s" % metadata.source >> - end >> - tmp.content >> - end >> - >> # Copy the values from the source to the resource. Yay. >> def copy_source_values >> devfail "Somehow got asked to copy source values without any >> metadata" unless metadata >> @@ -171,5 +161,32 @@ module Puppet >> resource[:check] = checks >> resource[:checksum] = :md5 unless resource.property(:checksum) >> end >> + >> + def local? >> + if found? and uri >> + return uri.scheme == "file" || uri.scheme == nil >> + end >> + false >> + end >> > > How about this (reading "||" as "defaults to"): > > def local? > found? and uri and (uri.scheme || "file") == "file" > end I'm always amazed by this kind of construction :-) Note that I don't find it readable... > >> + def full_path >> + if found? and uri >> + return URI.unescape(uri.path) >> + end >> + end >> + >> + def server >> + (uri and uri.host) or Puppet.settings[:server] >> + end >> + >> + def port >> + (uri and uri.port) or Puppet.settings[:masterport] >> + end >> + >> + private >> + >> + def uri >> + @uri ||= URI.parse(URI.escape(metadata.source)) >> + end >> end >> end >> diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb >> index b0dcf80..a7aeaea 100644 >> --- a/lib/puppet/util/checksums.rb >> +++ b/lib/puppet/util/checksums.rb >> @@ -39,11 +39,27 @@ module Puppet::Util::Checksums >> md5_file(filename, true) >> end >> >> + def md5_stream(&block) >> + require 'digest/md5' >> + digest = Digest::MD5.new() >> + yield digest >> + return digest.hexdigest >> + end >> + >> + alias :md5lite_stream :md5_stream >> + >> # Return the :mtime timestamp of a file. >> def mtime_file(filename) >> File.stat(filename).send(:mtime) >> end >> >> + # by definition this doesn't exist >> + def mtime_stream >> + nil >> + end >> + >> + alias :ctime_stream :mtime_stream >> + >> # Calculate a checksum using Digest::SHA1. >> def sha1(content) >> require 'digest/sha1' >> @@ -68,6 +84,15 @@ module Puppet::Util::Checksums >> sha1_file(filename, true) >> end >> >> + def sha1_stream >> + require 'digest/sha1' >> + digest = Digest::SHA1.new() >> + yield digest >> + return digest.hexdigest >> + end >> + >> + alias :sha1lite_stream :sha1_stream >> + >> # Return the :ctime of a file. >> def ctime_file(filename) >> File.stat(filename).send(:ctime) >> > > > > > Final thought: I think this has the most thorough test set of any commit > I've seen this year. That's certainly because I didn't produce many patches this year :-D -- Brice Figureau My Blog: http://www.masterzen.fr/ -- You received this message because you are subscribed to the Google Groups "Puppet Developers" group. To post to this group, send email to puppet-...@googlegroups.com. To unsubscribe from this group, send email to puppet-dev+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.