fpm-fry 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/fpm-fry +10 -0
- data/lib/cabin/nice_output.rb +70 -0
- data/lib/fpm/fry/block_enumerator.rb +25 -0
- data/lib/fpm/fry/build_output_parser.rb +22 -0
- data/lib/fpm/fry/client.rb +162 -0
- data/lib/fpm/fry/command/cook.rb +370 -0
- data/lib/fpm/fry/command.rb +90 -0
- data/lib/fpm/fry/detector.rb +109 -0
- data/lib/fpm/fry/docker_file.rb +149 -0
- data/lib/fpm/fry/joined_io.rb +63 -0
- data/lib/fpm/fry/os_db.rb +35 -0
- data/lib/fpm/fry/plugin/alternatives.rb +90 -0
- data/lib/fpm/fry/plugin/edit_staging.rb +66 -0
- data/lib/fpm/fry/plugin/exclude.rb +18 -0
- data/lib/fpm/fry/plugin/init.rb +53 -0
- data/lib/fpm/fry/plugin/platforms.rb +10 -0
- data/lib/fpm/fry/plugin/script_helper.rb +176 -0
- data/lib/fpm/fry/plugin/service.rb +100 -0
- data/lib/fpm/fry/plugin.rb +3 -0
- data/lib/fpm/fry/recipe/builder.rb +267 -0
- data/lib/fpm/fry/recipe.rb +141 -0
- data/lib/fpm/fry/source/dir.rb +56 -0
- data/lib/fpm/fry/source/git.rb +90 -0
- data/lib/fpm/fry/source/package.rb +202 -0
- data/lib/fpm/fry/source/patched.rb +118 -0
- data/lib/fpm/fry/source.rb +47 -0
- data/lib/fpm/fry/stream_parser.rb +98 -0
- data/lib/fpm/fry/tar.rb +71 -0
- data/lib/fpm/fry/templates/debian/after_install.erb +9 -0
- data/lib/fpm/fry/templates/debian/before_install.erb +13 -0
- data/lib/fpm/fry/templates/debian/before_remove.erb +13 -0
- data/lib/fpm/fry/templates/redhat/after_install.erb +2 -0
- data/lib/fpm/fry/templates/redhat/before_install.erb +6 -0
- data/lib/fpm/fry/templates/redhat/before_remove.erb +6 -0
- data/lib/fpm/fry/templates/sysv.erb +125 -0
- data/lib/fpm/fry/templates/upstart.erb +15 -0
- data/lib/fpm/fry/ui.rb +12 -0
- data/lib/fpm/package/docker.rb +186 -0
- metadata +111 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'fpm/fry/source'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'digest'
|
4
|
+
module FPM; module Fry ; module Source
|
5
|
+
class Dir
|
6
|
+
|
7
|
+
REGEX = %r!\A(?:file:|/|\.)!
|
8
|
+
|
9
|
+
def self.guess( url )
|
10
|
+
Source::guess_regex(REGEX, url)
|
11
|
+
end
|
12
|
+
|
13
|
+
class Cache < Struct.new(:package, :dir)
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
def_delegators :package, :url, :logger, :file_map
|
17
|
+
|
18
|
+
def tar_io
|
19
|
+
cmd = ['tar','-c','.']
|
20
|
+
logger.debug("Running tar",cmd: cmd, dir: dir)
|
21
|
+
::Dir.chdir(dir) do
|
22
|
+
return IO.popen(cmd)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def copy_to(dst)
|
27
|
+
children = ::Dir.new(dir).select{|x| x[0...1] != "." }.map{|x| File.join(dir,x) }
|
28
|
+
FileUtils.cp_r(children, dst)
|
29
|
+
end
|
30
|
+
|
31
|
+
def cachekey
|
32
|
+
dig = Digest::SHA2.new
|
33
|
+
tar_io.each(1024) do |block|
|
34
|
+
dig << block
|
35
|
+
end
|
36
|
+
return dig.hexdigest
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr :url, :logger, :file_map
|
41
|
+
|
42
|
+
def initialize( url, options = {} )
|
43
|
+
@url = URI(url)
|
44
|
+
if @url.relative?
|
45
|
+
@url.path = File.expand_path(@url.path)
|
46
|
+
end
|
47
|
+
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
48
|
+
@file_map = options.fetch(:file_map){ {'' => ''} }
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_cache(_)
|
52
|
+
Cache.new(self, url.path)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end ; end ; end
|
56
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'open3'
|
4
|
+
require 'fpm/fry/source'
|
5
|
+
module FPM; module Fry ; module Source
|
6
|
+
class Git
|
7
|
+
|
8
|
+
REGEX = %r!\A(?:git:|\S+@\S+:\S+\.git\z|https?:.*\.git\z|ssh:.*\.git\z)!
|
9
|
+
|
10
|
+
def self.guess( url )
|
11
|
+
Source::guess_regex(REGEX, url)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Cache < Struct.new(:package, :tempdir)
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
def_delegators :package, :url, :rev, :logger, :file_map
|
18
|
+
|
19
|
+
def update
|
20
|
+
begin
|
21
|
+
if !File.exists? repodir
|
22
|
+
if git('init', '--bare') != 0
|
23
|
+
raise "Initializing git repository failed"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
if git('fetch','--depth=1', url.to_s, rev) != 0
|
27
|
+
raise "Failed to fetch from remote #{url.to_s} ( #{rev} )"
|
28
|
+
end
|
29
|
+
return self
|
30
|
+
rescue Errno::ENOENT
|
31
|
+
raise "Cannot find git binary. Is it installed?"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def tar_io
|
36
|
+
cmd = [package.git, "--git-dir=#{repodir}",'archive','--format=tar','FETCH_HEAD']
|
37
|
+
logger.debug("Running git",cmd: cmd)
|
38
|
+
IO.popen(cmd)
|
39
|
+
end
|
40
|
+
|
41
|
+
def copy_to(dst)
|
42
|
+
cmd = [package.git, "--git-dir=#{repodir}", "--work-tree=#{dst}",'checkout','FETCH_HEAD','--','*']
|
43
|
+
logger.debug("Running git",cmd: cmd)
|
44
|
+
system(*cmd, chdir: dst)
|
45
|
+
end
|
46
|
+
|
47
|
+
def cachekey
|
48
|
+
cmd = [package.git, "--git-dir=#{repodir}",'rev-parse','FETCH_HEAD^{tree}']
|
49
|
+
logger.debug("Running git",cmd: cmd)
|
50
|
+
return IO.popen(cmd).read.chomp
|
51
|
+
end
|
52
|
+
private
|
53
|
+
def repodir
|
54
|
+
File.join(tempdir,File.basename(url.path))
|
55
|
+
end
|
56
|
+
|
57
|
+
def git(*args)
|
58
|
+
cmd = [package.git, "--git-dir=#{repodir}",*args]
|
59
|
+
logger.debug("Running git",cmd: cmd.join(' '))
|
60
|
+
Open3.popen3(*cmd) do |sin, out, err, thr|
|
61
|
+
sin.close
|
62
|
+
out.each_line do |line|
|
63
|
+
logger.debug(line.chomp)
|
64
|
+
end
|
65
|
+
err.each_line do |line|
|
66
|
+
logger.debug(line.chomp)
|
67
|
+
end
|
68
|
+
return thr.value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
attr :logger, :git, :rev, :file_map, :url
|
75
|
+
|
76
|
+
def initialize( url, options = {} )
|
77
|
+
url = url.sub(/\A(\S+@\S+):(\S+\.git)\z/,'ssh://\1/\2')
|
78
|
+
@url = URI(url)
|
79
|
+
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
80
|
+
@rev = options[:branch] || options[:tag] || options[:rev] || 'HEAD'
|
81
|
+
@file_map = options.fetch(:file_map){ {'' => ''} }
|
82
|
+
@git = options[:git] || 'git'
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_cache(tempdir)
|
86
|
+
Cache.new(self, tempdir).update
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end ; end ; end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'digest'
|
3
|
+
require 'net/http'
|
4
|
+
require 'forwardable'
|
5
|
+
require 'zlib'
|
6
|
+
require 'fpm/fry/source'
|
7
|
+
module FPM; module Fry ; module Source
|
8
|
+
class Package
|
9
|
+
|
10
|
+
REGEX = %r!\Ahttps?:!
|
11
|
+
|
12
|
+
def self.guess( url )
|
13
|
+
Source::guess_regex(REGEX, url)
|
14
|
+
end
|
15
|
+
|
16
|
+
class RedirectError < CacheFailed
|
17
|
+
end
|
18
|
+
|
19
|
+
class Cache < Struct.new(:package,:tempdir)
|
20
|
+
extend Forwardable
|
21
|
+
|
22
|
+
def_delegators :package, :url, :checksum, :checksum_algorithm, :agent, :logger, :file_map
|
23
|
+
|
24
|
+
def initialize(*_)
|
25
|
+
super
|
26
|
+
if !checksum
|
27
|
+
update!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cache_valid?
|
32
|
+
c = @observed_checksum || checksum
|
33
|
+
begin
|
34
|
+
checksum_algorithm.file(tempfile).hexdigest == c
|
35
|
+
rescue Errno::ENOENT
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def update!
|
41
|
+
if cache_valid?
|
42
|
+
logger.debug("Found valid cache", url: url, tempfile: tempfile)
|
43
|
+
return
|
44
|
+
end
|
45
|
+
d = checksum_algorithm.new
|
46
|
+
f = nil
|
47
|
+
fetch_url(url) do |resp|
|
48
|
+
begin
|
49
|
+
f = File.new(tempfile,'w')
|
50
|
+
resp.read_body do | chunk |
|
51
|
+
d.update(chunk)
|
52
|
+
f.write(chunk)
|
53
|
+
end
|
54
|
+
rescue => e
|
55
|
+
raise CacheFailed, e
|
56
|
+
ensure
|
57
|
+
f.close
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@observed_checksum = d.hexdigest
|
62
|
+
logger.debug("got checksum", checksum: @observed_checksum)
|
63
|
+
if checksum
|
64
|
+
if d.hexdigest != checksum
|
65
|
+
raise CacheFailed.new("Checksum failed",given: d.hexdigest, expected: checksum)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
return true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def fetch_url( url, redirs = 3, &block)
|
73
|
+
url = URI(url.to_s) unless url.kind_of? URI
|
74
|
+
Net::HTTP.get_response(url) do |resp|
|
75
|
+
case(resp)
|
76
|
+
when Net::HTTPRedirection
|
77
|
+
if redirs == 0
|
78
|
+
raise RedirectError, "Too many redirects"
|
79
|
+
end
|
80
|
+
logger.debug("Following redirect", url: url.to_s , location: resp['location'])
|
81
|
+
return fetch_url( resp['location'], redirs - 1, &block)
|
82
|
+
when Net::HTTPSuccess
|
83
|
+
return block.call(resp)
|
84
|
+
else
|
85
|
+
raise CacheFailed.new('Unable to fetch file',url: url.to_s, http_code: resp.code, http_message: resp.message)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def tempfile
|
91
|
+
File.join(tempdir,File.basename(url.path))
|
92
|
+
end
|
93
|
+
|
94
|
+
def cachekey
|
95
|
+
@observed_checksum || checksum
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class TarCache < Cache
|
101
|
+
|
102
|
+
def tar_io
|
103
|
+
update!
|
104
|
+
ioclass.open(tempfile)
|
105
|
+
end
|
106
|
+
|
107
|
+
def copy_to(dst)
|
108
|
+
update!
|
109
|
+
cmd = ['tar','-xf',tempfile,'-C',dst]
|
110
|
+
logger.debug("Running tar",cmd: cmd)
|
111
|
+
system(*cmd)
|
112
|
+
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
def ioclass
|
116
|
+
File
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class TarGzCache < TarCache
|
121
|
+
protected
|
122
|
+
|
123
|
+
def ioclass
|
124
|
+
Zlib::GzipReader
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class ZipCache < Cache
|
129
|
+
|
130
|
+
def tar_io
|
131
|
+
if !::File.directory?( unpacked_tmpdir )
|
132
|
+
workdir = unpacked_tmpdir + '.tmp'
|
133
|
+
begin
|
134
|
+
FileUtils.mkdir(workdir)
|
135
|
+
rescue Errno::EEXIST
|
136
|
+
FileUtils.rm_rf(workdir)
|
137
|
+
FileUtils.mkdir(workdir)
|
138
|
+
end
|
139
|
+
copy_to( workdir )
|
140
|
+
File.rename(workdir, unpacked_tmpdir)
|
141
|
+
end
|
142
|
+
cmd = ['tar','-c','.']
|
143
|
+
logger.debug("Running tar",cmd: cmd, dir: unpacked_tmpdir)
|
144
|
+
::Dir.chdir(unpacked_tmpdir) do
|
145
|
+
return IO.popen(cmd)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def copy_to(dst)
|
150
|
+
update!
|
151
|
+
cmd = ['unzip', tempfile, '-d', dst ]
|
152
|
+
logger.debug("Running unzip",cmd: cmd)
|
153
|
+
system(*cmd, out: '/dev/null')
|
154
|
+
end
|
155
|
+
|
156
|
+
def unpacked_tmpdir
|
157
|
+
File.join(tempdir, cachekey)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
CACHE_CLASSES = {
|
162
|
+
'.tar' => TarCache,
|
163
|
+
'.tar.gz' => TarGzCache,
|
164
|
+
'.tgz' => TarGzCache,
|
165
|
+
'.zip' => ZipCache
|
166
|
+
}
|
167
|
+
|
168
|
+
attr :file_map, :data, :url, :extension, :checksum, :checksum_algorithm, :agent, :logger
|
169
|
+
|
170
|
+
def initialize( url, options = {} )
|
171
|
+
@url = URI(url)
|
172
|
+
@extension = options.fetch(:extension){
|
173
|
+
CACHE_CLASSES.keys.find{|ext|
|
174
|
+
@url.path.end_with?(ext)
|
175
|
+
}
|
176
|
+
}
|
177
|
+
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
178
|
+
@checksum = options[:checksum]
|
179
|
+
@checksum_algorithm = guess_checksum_algorithm(options[:checksum])
|
180
|
+
@file_map = options.fetch(:file_map){ {'' => ''} }
|
181
|
+
end
|
182
|
+
|
183
|
+
def build_cache(tempdir)
|
184
|
+
CACHE_CLASSES.fetch(extension).new(self, tempdir)
|
185
|
+
end
|
186
|
+
private
|
187
|
+
|
188
|
+
def guess_checksum_algorithm( checksum )
|
189
|
+
case(checksum)
|
190
|
+
when nil
|
191
|
+
return Digest::SHA256
|
192
|
+
when /\A(sha256:)?[0-9a-f]{64}\z/ then
|
193
|
+
return Digest::SHA256
|
194
|
+
when /\A(sha1:)?[0-9a-f]{40}\z/ then
|
195
|
+
return Digest::SHA1
|
196
|
+
else
|
197
|
+
raise "Unknown checksum algorithm"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end end end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'fpm/fry/tar'
|
2
|
+
require 'digest'
|
3
|
+
module FPM; module Fry ; module Source
|
4
|
+
class Patched
|
5
|
+
|
6
|
+
class Cache < Struct.new(:package, :tmpdir)
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :package, :logger, :file_map
|
10
|
+
|
11
|
+
attr :inner
|
12
|
+
|
13
|
+
def initialize(*_)
|
14
|
+
@updated = false
|
15
|
+
super
|
16
|
+
@inner = package.inner.build_cache(tmpdir)
|
17
|
+
end
|
18
|
+
|
19
|
+
def update!
|
20
|
+
@updated ||= begin
|
21
|
+
if !File.directory?(unpacked_tmpdir)
|
22
|
+
workdir = unpacked_tmpdir + '.tmp'
|
23
|
+
begin
|
24
|
+
FileUtils.mkdir(workdir)
|
25
|
+
rescue Errno::EEXIST
|
26
|
+
FileUtils.rm_rf(workdir)
|
27
|
+
FileUtils.mkdir(workdir)
|
28
|
+
end
|
29
|
+
if inner.respond_to? :copy_to
|
30
|
+
inner.copy_to(workdir)
|
31
|
+
else
|
32
|
+
ex = Tar::Extractor.new(logger: logger)
|
33
|
+
tio = inner.tar_io
|
34
|
+
begin
|
35
|
+
ex.extract(workdir, ::Gem::Package::TarReader.new(tio), chown: false)
|
36
|
+
ensure
|
37
|
+
tio.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
package.patches.each do |patch|
|
41
|
+
cmd = ['patch','-p1','-i',patch[:file]]
|
42
|
+
chdir = File.expand_path(patch.fetch(:chdir,'.'),workdir)
|
43
|
+
logger.debug("Running patch",cmd: cmd, dir: chdir )
|
44
|
+
system(*cmd, chdir: chdir, out: :close)
|
45
|
+
end
|
46
|
+
File.rename(workdir, unpacked_tmpdir)
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
private :update!
|
52
|
+
|
53
|
+
def tar_io
|
54
|
+
update!
|
55
|
+
cmd = ['tar','-c','.']
|
56
|
+
logger.debug("Running tar",cmd: cmd, dir: unpacked_tmpdir)
|
57
|
+
# IO.popen( ..., chdir: ... ) doesn't work on older ruby
|
58
|
+
::Dir.chdir(unpacked_tmpdir) do
|
59
|
+
return IO.popen(cmd)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def cachekey
|
64
|
+
dig = Digest::SHA2.new
|
65
|
+
dig << inner.cachekey << "\x00"
|
66
|
+
package.patches.each do |patch|
|
67
|
+
dig.file(patch[:file])
|
68
|
+
dig << "\x00"
|
69
|
+
end
|
70
|
+
return dig.hexdigest
|
71
|
+
end
|
72
|
+
|
73
|
+
def unpacked_tmpdir
|
74
|
+
File.join(tmpdir, cachekey)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
attr :inner, :patches
|
80
|
+
|
81
|
+
extend Forwardable
|
82
|
+
|
83
|
+
def_delegators :inner, :logger, :file_map
|
84
|
+
|
85
|
+
def initialize( inner , options = {})
|
86
|
+
@inner = inner
|
87
|
+
@patches = Array(options[:patches]).map do |file|
|
88
|
+
if file.kind_of? String
|
89
|
+
options = {file: file}
|
90
|
+
elsif file.kind_of? Hash
|
91
|
+
options = file.dup
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Expected a Hash or a String, got #{file.inspect}"
|
94
|
+
end
|
95
|
+
options[:file] = File.expand_path(options[:file])
|
96
|
+
if !File.exists?(options[:file])
|
97
|
+
raise ArgumentError, "File doesn't exist: #{options[:file]}"
|
98
|
+
end
|
99
|
+
options
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_cache(tmpdir)
|
104
|
+
Cache.new(self,tmpdir)
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.decorate(options)
|
108
|
+
if options.key?(:patches) && Array(options[:patches]).size > 0
|
109
|
+
p = options.delete(:patches)
|
110
|
+
return new( yield(options), patches: p )
|
111
|
+
else
|
112
|
+
return yield options
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end ; end ; end
|
118
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module FPM; module Fry ; module Source
|
2
|
+
|
3
|
+
class CacheFailed < StandardError
|
4
|
+
|
5
|
+
attr :options
|
6
|
+
|
7
|
+
def initialize(e, opts = {})
|
8
|
+
if e.kind_of? Exception
|
9
|
+
@options = {reason: e}.merge opts
|
10
|
+
super(e.message)
|
11
|
+
else
|
12
|
+
@options = opts.dup
|
13
|
+
super(e.to_s)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Null
|
19
|
+
|
20
|
+
module Cache
|
21
|
+
def self.tar_io
|
22
|
+
StringIO.new("\x00"*1024)
|
23
|
+
end
|
24
|
+
def self.file_map
|
25
|
+
return {}
|
26
|
+
end
|
27
|
+
def self.cachekey
|
28
|
+
return '0' * 32
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.build_cache(*_)
|
33
|
+
return Cache
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
def guess_regex(rx, url)
|
41
|
+
if m = rx.match(url.to_s)
|
42
|
+
return m[0].size
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end ; end ; end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'excon/middlewares/base'
|
2
|
+
module FPM; module Fry
|
3
|
+
class StreamParser
|
4
|
+
|
5
|
+
class ShortRead < EOFError
|
6
|
+
end
|
7
|
+
|
8
|
+
class Instance < Excon::Middleware::Base
|
9
|
+
|
10
|
+
def initialize(stack, parser)
|
11
|
+
super(stack)
|
12
|
+
@parser = parser
|
13
|
+
end
|
14
|
+
|
15
|
+
def response_call(datum)
|
16
|
+
if datum[:response]
|
17
|
+
# probably mocked
|
18
|
+
if datum[:response][:body]
|
19
|
+
@parser.parse(StringIO.new(datum[:response][:body]))
|
20
|
+
end
|
21
|
+
return @stack.response_call(datum)
|
22
|
+
else
|
23
|
+
socket = datum[:connection].send(:socket)
|
24
|
+
begin
|
25
|
+
line = socket.readline
|
26
|
+
match = /^HTTP\/\d+\.\d+\s(\d{3})\s/.match(line)
|
27
|
+
end while !match
|
28
|
+
status = match[1].to_i
|
29
|
+
|
30
|
+
datum[:response] = {
|
31
|
+
:body => '',
|
32
|
+
:headers => Excon::Headers.new,
|
33
|
+
:status => status,
|
34
|
+
:remote_ip => socket.respond_to?(:remote_ip) && socket.remote_ip,
|
35
|
+
}
|
36
|
+
Excon::Response.parse_headers(socket, datum)
|
37
|
+
|
38
|
+
@parser.parse(socket)
|
39
|
+
return @stack.response_call(datum)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
attr :out, :err
|
46
|
+
|
47
|
+
def initialize(out, err)
|
48
|
+
@out, @err = out, err
|
49
|
+
@state = :null
|
50
|
+
@left = 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def new(stack)
|
54
|
+
Instance.new(stack, self)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse(socket)
|
58
|
+
left = 0
|
59
|
+
streams = {1 => out, 2 => err}
|
60
|
+
loop do
|
61
|
+
type = read_exactly(socket,4){|part|
|
62
|
+
if part.bytesize == 0
|
63
|
+
return
|
64
|
+
else
|
65
|
+
raise ShortRead
|
66
|
+
end
|
67
|
+
}.unpack("c".freeze)[0]
|
68
|
+
stream = streams.fetch(type){ raise ArgumentError, "Wrong stream type: #{type}"}
|
69
|
+
len = read_exactly(socket,4).unpack('I>')[0]
|
70
|
+
while len > 0
|
71
|
+
chunk = socket.read([64,len].min)
|
72
|
+
raise ShortRead if chunk.nil?
|
73
|
+
len -= chunk.bytesize
|
74
|
+
stream.write(chunk)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def read_exactly(socket, len)
|
80
|
+
buf = ""
|
81
|
+
left = len
|
82
|
+
while left != 0
|
83
|
+
read = socket.read(left)
|
84
|
+
if read.nil?
|
85
|
+
if block_given?
|
86
|
+
yield buf
|
87
|
+
else
|
88
|
+
raise ShortRead
|
89
|
+
end
|
90
|
+
end
|
91
|
+
buf << read
|
92
|
+
left = len - buf.bytesize
|
93
|
+
end
|
94
|
+
return buf
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end ; end
|
data/lib/fpm/fry/tar.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module FPM; module Fry; end ; end
|
2
|
+
|
3
|
+
class FPM::Fry::Tar
|
4
|
+
|
5
|
+
class Extractor
|
6
|
+
|
7
|
+
def initialize( options = {} )
|
8
|
+
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
9
|
+
end
|
10
|
+
|
11
|
+
def extract(destdir, reader, options = {})
|
12
|
+
reader.each do |entry|
|
13
|
+
extract_entry(File.join(destdir, entry.full_name), entry, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def extract_entry(dest, entry, options = {})
|
18
|
+
full_name = entry.full_name
|
19
|
+
mode = entry.header.mode
|
20
|
+
|
21
|
+
destdir = File.dirname(dest)
|
22
|
+
uid = map_user(entry.header.uid, entry.header.uname)
|
23
|
+
gid = map_group(entry.header.gid, entry.header.gname)
|
24
|
+
|
25
|
+
@logger.debug('Extracting','file' => dest, 'uid'=> uid, 'gid' => gid, 'entry.fullname' => full_name, 'entry.mode' => mode )
|
26
|
+
|
27
|
+
case(entry.header.typeflag)
|
28
|
+
when "5" # Directory
|
29
|
+
FileUtils.mkdir_p(dest, :mode => mode)
|
30
|
+
when "2" # Symlink
|
31
|
+
destdir = File.dirname(dest)
|
32
|
+
FileUtils.mkdir_p(destdir, :mode => 0755)
|
33
|
+
File.symlink( entry.header.linkname, dest )
|
34
|
+
when "0" # File
|
35
|
+
destdir = File.dirname(dest)
|
36
|
+
FileUtils.mkdir_p(destdir, :mode => 0755)
|
37
|
+
File.open(dest, "wb", entry.header.mode) do |os|
|
38
|
+
loop do
|
39
|
+
data = entry.read(4096)
|
40
|
+
break unless data
|
41
|
+
os.write(data)
|
42
|
+
end
|
43
|
+
os.fsync
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@logger.warn('Ignoring unknown tar entry',name: full_name)
|
47
|
+
return
|
48
|
+
end
|
49
|
+
FileUtils.chmod(entry.header.mode, dest)
|
50
|
+
chown( uid, gid, dest ) if options.fetch(:chown,true)
|
51
|
+
end
|
52
|
+
|
53
|
+
def chown( uid, gid, path )
|
54
|
+
FileUtils.chown( uid, gid, path )
|
55
|
+
rescue Errno::EPERM
|
56
|
+
@logger.warn('Unable to chown file', 'file' => path, 'uid' => uid, 'gid' => gid)
|
57
|
+
end
|
58
|
+
|
59
|
+
def map_user( uid, _ )
|
60
|
+
return uid
|
61
|
+
end
|
62
|
+
|
63
|
+
def map_group( gid, _ )
|
64
|
+
return gid
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|