Jira (PUP-9043) Downloaded files are sometimes truncated

3 views
Skip to first unread message

Josh Cooper (JIRA)

unread,
Aug 8, 2019, 5:09:03 PM8/8/19
to puppe...@googlegroups.com
Josh Cooper updated an issue
 
Puppet / Bug PUP-9043
Downloaded files are sometimes truncated
Change By: Josh Cooper
Summary: file resource - mtime checksum does not seem to work Downloaded files are sometimes truncated
Add Comment Add Comment
 
This message was sent by Atlassian JIRA (v7.7.1#77002-sha1:e75ca93)
Atlassian logo

Josh Cooper (JIRA)

unread,
Aug 9, 2019, 7:34:03 PM8/9/19
to puppe...@googlegroups.com
Josh Cooper commented on Bug PUP-9043
 
Re: Downloaded files are sometimes truncated

We could protect against the ruby bug for file sources (either puppet or http*):

diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb
index df8fa95751..8f48f9865b 100644
--- a/lib/puppet/type/file/source.rb
+++ b/lib/puppet/type/file/source.rb
@@ -323,7 +323,25 @@ module Puppet
     def chunk_file_from_source
       get_from_source do |response|
         case response.code
-        when /^2/;  uncompress(response) { |uncompressor| response.read_body { |chunk| yield uncompressor.uncompress(chunk) } }
+        when /^2/
+          truncated = false
+          uncompress(response) do |uncompressor|
+            response.read_body do |data|
+              # If server closes the connection, Ruby will swallow EOFError
+              # https://bugs.ruby-lang.org/issues/14972 So verify we received
+              # all of the data as indicated by Content-Length. However, it
+              # will be omitted when using chunked transfer encoding.
+              clen = response['Content-Length']
+              if clen && clen.to_i != data.length
+                truncated = true
+              end
+
+              yield uncompressor.uncompress(data)
+            end
+          end
+
+          # Have to raise after `read_body` returns
+          raise EOFError if truncated
         else
           # Raise the http error if we didn't get a 'success' of some kind.
           message = "Error #{response.code} on SERVER: #{(response.body||'').empty? ? response.message : uncompress_body(response)}"

But it wouldn't solve the other places where we use Net::HTTP like the rest terminus. So patching ruby is preferable. I was also able to reproduce using toxiproxy. Agent, toxiproxy and puppetserver are on one host.

Added file resource to site.pp

file { '/tmp/ruby.tar.gz':
  ensure => file,
  source => 'http://cache.ruby-lang.org:8888/pub/ruby/2.6/ruby-2.6.3.tar.gz'
}

On the agent, edited {{/etc/hosts}

On the agent:

$ toxiproxy-server
...

In other shell on the agent, enabled TCP proxies for puppet and ruby:

$ toxiproxy-cli create puppet -l localhost:8080 -u localhost:8140
$ toxiproxy-cli create ruby -l localhost:8888 -u 151.101.193.178:80

And added a limit_data toxic to the downstream connection between agent and proxy:

$ toxiproxy-cli t add ruby  -t limit_data -a bytes=1000

Agent downloads truncated file due to ignored EOF:

$ rm -f /tmp/ruby.tar.gz && bx puppet agent -t --masterport 8080
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Caching catalog for whatcom
Info: Applying configuration version '1565393439'
Notice: /Stage[main]/Foo/File[/tmp/ruby.tar.gz]/ensure: defined content as '{mtime}2019-04-17 15:25:09 UTC' (corrective)
Notice: Applied catalog in 0.12 seconds
$ ls -la /tmp/ruby.tar.gz
-rw-r--r--  1 josh  wheel   404B Aug  9 16:30 /tmp/ruby.tar.gz

With the patch I get:

rm -f /tmp/ruby.tar.gz && bx puppet agent -t --masterport 8080
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Caching catalog for whatcom
Info: Applying configuration version '1565393533'
Error: Could not set 'file' on ensure: EOFError (file: /etc/puppetlabs/code/environments/production/modules/foo/manifests/init.pp, line: 2)
Error: Could not set 'file' on ensure: EOFError (file: /etc/puppetlabs/code/environments/production/modules/foo/manifests/init.pp, line: 2)
Wrapped exception:
EOFError
Error: /Stage[main]/Foo/File[/tmp/ruby.tar.gz]/ensure: change from 'absent' to 'file' failed: Could not set 'file' on ensure: EOFError (file: /etc/puppetlabs/code/environments/production/modules/foo/manifests/init.pp, line: 2) (corrective)
Notice: Applied catalog in 0.15 seconds

Note the double EOFError is because Net::HTTP silently attempts to retry failed idempotent requests, and it's not configurable until ruby 2.5. See PUP-3905.

Josh Cooper (JIRA)

unread,
Aug 12, 2019, 6:33:17 PM8/12/19
to puppe...@googlegroups.com
Josh Cooper commented on Bug PUP-9043

I confirmed the ruby behavior is definitely incorrect, from https://tools.ietf.org/html/rfc7230#section-3.4

A message that uses a valid Content-Length is incomplete
if the size of the message body received (in octets) is less than the
value given by Content-Length.

I'm going to patch our vendored ruby and push on getting the ruby PR merged upstream.

Reply all
Reply to author
Forward
0 new messages