fpm-fry 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|