fpm-fry 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cabin/nice_output.rb +11 -2
- data/lib/fpm/fry/channel.rb +23 -0
- data/lib/fpm/fry/client.rb +0 -25
- data/lib/fpm/fry/command/cook.rb +51 -24
- data/lib/fpm/fry/command.rb +17 -9
- data/lib/fpm/fry/detector.rb +13 -5
- data/lib/fpm/fry/docker_file.rb +8 -4
- data/lib/fpm/fry/plugin/config.rb +90 -0
- data/lib/fpm/fry/plugin/same_version.rb +19 -0
- data/lib/fpm/fry/plugin/service.rb +49 -4
- data/lib/fpm/fry/plugin/user.rb +21 -0
- data/lib/fpm/fry/recipe/builder.rb +21 -8
- data/lib/fpm/fry/recipe.rb +15 -2
- data/lib/fpm/fry/templates/sysv.erb +18 -1
- data/lib/fpm/fry/templates/upstart.erb +9 -0
- data/lib/fpm/fry/ui.rb +2 -1
- data/lib/fpm/package/docker.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d58721a3f79317f65181422ff3a7e5d28ef823e
|
4
|
+
data.tar.gz: 85229c608182080d92b67b81c2e6d09f701d19ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fc67b6dcbe5d6d8944028721665cbee9ae9bdb4f615c68812e012063e66c22ab278b943dc155d1e2ecc58e23021af133e8c0f29185db0ba0b5faa4f2dba5e5e
|
7
|
+
data.tar.gz: 03c12a7c8743715a8a33fff09c84e423ef67cbcd6a4a3a8659a6f7a1bd0b2e361797932117478950fb6ca6e5ac9af35049a4087c094a521b16a3e16a8d1fe22a
|
data/lib/cabin/nice_output.rb
CHANGED
@@ -6,14 +6,16 @@ class Cabin::NiceOutput
|
|
6
6
|
:red => "\e[1;31m",
|
7
7
|
:green => "\e[1;32m",
|
8
8
|
:yellow => "\e[0;33m",
|
9
|
-
:white => "\e[0;37m"
|
9
|
+
:white => "\e[0;37m",
|
10
|
+
:blue => "\e[1;34m"
|
10
11
|
}
|
11
12
|
|
12
13
|
DIM_CODEMAP = {
|
13
14
|
red: "\e[0;31m",
|
14
15
|
green: "\e[0;32m",
|
15
16
|
white: "\e[1;30m",
|
16
|
-
yellow: "\e[33m"
|
17
|
+
yellow: "\e[33m",
|
18
|
+
blue: "\e[0;34m"
|
17
19
|
}
|
18
20
|
|
19
21
|
LEVELMAP = {
|
@@ -21,6 +23,7 @@ class Cabin::NiceOutput
|
|
21
23
|
:error => :red,
|
22
24
|
:warn => :yellow,
|
23
25
|
:info => :green,
|
26
|
+
:hint => :blue,
|
24
27
|
:debug => :white,
|
25
28
|
}
|
26
29
|
|
@@ -43,6 +46,9 @@ class Cabin::NiceOutput
|
|
43
46
|
bold = data.delete(:bold) ? :bold : nil
|
44
47
|
|
45
48
|
backtrace = data.delete(:backtrace)
|
49
|
+
if !backtrace && data[:exception].respond_to?(:backtrace)
|
50
|
+
backtrace = data[:exception].backtrace
|
51
|
+
end
|
46
52
|
|
47
53
|
# Make 'error' and other log levels have color
|
48
54
|
if color.nil?
|
@@ -52,6 +58,9 @@ class Cabin::NiceOutput
|
|
52
58
|
message = [event[:level] ? '====> ' : ' ',event[:message]]
|
53
59
|
message.unshift(CODEMAP[color.to_sym]) if !color.nil?
|
54
60
|
message << DIM_CODEMAP[color] if !color.nil?
|
61
|
+
if documentation = data.delete(:documentation)
|
62
|
+
message << "\n\tRead more on this topic here: #{documentation}"
|
63
|
+
end
|
55
64
|
if data.any?
|
56
65
|
message << "\n" << pp(data)
|
57
66
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'cabin/channel'
|
2
|
+
module FPM; module Fry
|
3
|
+
class Channel < Cabin::Channel
|
4
|
+
|
5
|
+
module Hint
|
6
|
+
def hint( message, data = {} )
|
7
|
+
return unless hint?
|
8
|
+
log(message, data.merge(level: :hint))
|
9
|
+
end
|
10
|
+
|
11
|
+
def hint?
|
12
|
+
!defined?(@hint) || @hint
|
13
|
+
end
|
14
|
+
|
15
|
+
def hint=( bool )
|
16
|
+
@hint = !!bool
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
include Hint
|
21
|
+
|
22
|
+
end
|
23
|
+
end ; end
|
data/lib/fpm/fry/client.rb
CHANGED
@@ -9,30 +9,6 @@ module FPM; module Fry; end ; end
|
|
9
9
|
|
10
10
|
class FPM::Fry::Client
|
11
11
|
|
12
|
-
class LogInstrumentor < Struct.new(:logger)
|
13
|
-
|
14
|
-
def instrument(event, data = {})
|
15
|
-
if block_given?
|
16
|
-
logger.debug('Requesting HTTP', filtered(data))
|
17
|
-
r = yield
|
18
|
-
return r
|
19
|
-
else
|
20
|
-
logger.debug('Getting HTTP response', filtered(data))
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def filtered(data)
|
25
|
-
filtered = {}
|
26
|
-
filtered[:path] = data[:path] if data[:path]
|
27
|
-
filtered[:verb] = data[:method] if data[:method]
|
28
|
-
filtered[:status] = data[:status] if data[:status]
|
29
|
-
filtered[:body] = data[:body][0..500] if data[:body]
|
30
|
-
filtered[:headers] = data[:headers]
|
31
|
-
return filtered
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
12
|
class FileNotFound < StandardError
|
37
13
|
end
|
38
14
|
|
@@ -141,7 +117,6 @@ class FPM::Fry::Client
|
|
141
117
|
def agent_for( uri, tls )
|
142
118
|
proto, address = uri.split('://',2)
|
143
119
|
options = {
|
144
|
-
instrumentor: LogInstrumentor.new(logger),
|
145
120
|
read_timeout: 10000
|
146
121
|
}.merge( tls )
|
147
122
|
case(proto)
|
data/lib/fpm/fry/command/cook.rb
CHANGED
@@ -18,10 +18,6 @@ module FPM; module Fry
|
|
18
18
|
parameter 'image', 'Docker image to build from'
|
19
19
|
parameter '[recipe]', 'Recipe file to cook', default: 'recipe.rb'
|
20
20
|
|
21
|
-
attr :ui
|
22
|
-
extend Forwardable
|
23
|
-
def_delegators :ui, :out, :err, :logger, :tmpdir
|
24
|
-
|
25
21
|
def initialize(invocation_path, ctx = {}, parent_attribute_values = {})
|
26
22
|
@tls = nil
|
27
23
|
require 'digest'
|
@@ -35,10 +31,6 @@ module FPM; module Fry
|
|
35
31
|
require 'fpm/fry/block_enumerator'
|
36
32
|
require 'fpm/fry/build_output_parser'
|
37
33
|
super
|
38
|
-
@ui = ctx.fetch(:ui){ UI.new }
|
39
|
-
if debug?
|
40
|
-
ui.logger.level = :debug
|
41
|
-
end
|
42
34
|
end
|
43
35
|
|
44
36
|
def detector
|
@@ -53,12 +45,8 @@ module FPM; module Fry
|
|
53
45
|
end
|
54
46
|
|
55
47
|
def detector=(d)
|
56
|
-
|
57
|
-
|
58
|
-
raise "Unable to detect distribution from given image"
|
59
|
-
end
|
60
|
-
rescue Excon::Errors::NotFound
|
61
|
-
raise "Image not found"
|
48
|
+
unless d.detect!
|
49
|
+
raise "Unable to detect distribution from given image"
|
62
50
|
end
|
63
51
|
@detector = d
|
64
52
|
end
|
@@ -70,7 +58,7 @@ module FPM; module Fry
|
|
70
58
|
|
71
59
|
def output_class
|
72
60
|
@output_class ||= begin
|
73
|
-
logger.
|
61
|
+
logger.debug("Autodetecting package type",flavour: flavour)
|
74
62
|
case(flavour)
|
75
63
|
when 'debian'
|
76
64
|
require 'fpm/package/deb'
|
@@ -92,7 +80,7 @@ module FPM; module Fry
|
|
92
80
|
distribution_version: detector.version,
|
93
81
|
flavour: flavour
|
94
82
|
}
|
95
|
-
logger.
|
83
|
+
logger.debug("Loading recipe",variables: vars, recipe: recipe)
|
96
84
|
b = Recipe::Builder.new(vars, Recipe.new, logger: ui.logger)
|
97
85
|
b.load_file( recipe )
|
98
86
|
b
|
@@ -150,7 +138,7 @@ module FPM; module Fry
|
|
150
138
|
'Content-Type'=>'application/tar'
|
151
139
|
},
|
152
140
|
expects: [200],
|
153
|
-
path: client.url("build?rm=1&t=#{cachetag}"),
|
141
|
+
path: client.url("build?rm=1&dockerfile=#{DockerFile::NAME}&t=#{cachetag}"),
|
154
142
|
request_block: BlockEnumerator.new(df.tar_io)
|
155
143
|
)
|
156
144
|
end
|
@@ -162,12 +150,12 @@ module FPM; module Fry
|
|
162
150
|
'Content-Type'=>'application/tar'
|
163
151
|
},
|
164
152
|
expects: [200],
|
165
|
-
path: client.url(
|
153
|
+
path: client.url("build?rm=1&dockerfile=#{DockerFile::NAME}"),
|
166
154
|
request_block: BlockEnumerator.new(df.tar_io),
|
167
155
|
response_block: parser
|
168
156
|
)
|
169
157
|
if parser.images.none?
|
170
|
-
raise "
|
158
|
+
raise "Didn't find a build image in the stream. This usually means that the build script failed."
|
171
159
|
end
|
172
160
|
image = parser.images.last
|
173
161
|
logger.debug("Detected build image", image: image)
|
@@ -191,13 +179,12 @@ module FPM; module Fry
|
|
191
179
|
begin
|
192
180
|
client.read( container, '/var/lib/apt/lists') do |file|
|
193
181
|
next if file.header.name == 'lists/'
|
194
|
-
logger.
|
195
|
-
return
|
182
|
+
logger.hint("/var/lib/apt/lists is not empty, you could try to speed up builds with --update=never", documentation: 'https://github.com/xing/fpm-fry/wiki/The-update-parameter')
|
183
|
+
return true
|
196
184
|
end
|
197
185
|
ensure
|
198
186
|
client.delete(path: client.url('containers',container))
|
199
187
|
end
|
200
|
-
logger.info("/var/lib/apt/lists is empty, update is required ( disable update with --apt-update=never )")
|
201
188
|
return true
|
202
189
|
when 'always'
|
203
190
|
return true
|
@@ -250,7 +237,7 @@ module FPM; module Fry
|
|
250
237
|
)
|
251
238
|
json = JSON.parse(res.body)
|
252
239
|
if json["StatusCode"] != 0
|
253
|
-
raise "Build failed"
|
240
|
+
raise "Build failed with exit code #{json["StatusCode"]}"
|
254
241
|
end
|
255
242
|
return yield container
|
256
243
|
ensure
|
@@ -283,6 +270,12 @@ module FPM; module Fry
|
|
283
270
|
|
284
271
|
output.output(tmp_package_file)
|
285
272
|
|
273
|
+
if output.config_files.any?
|
274
|
+
logger.debug("Found config files for #{output.name}", files: output.config_files)
|
275
|
+
else
|
276
|
+
logger.debug("No config files for #{output.name}")
|
277
|
+
end
|
278
|
+
|
286
279
|
begin
|
287
280
|
FileUtils.rm_rf package_file
|
288
281
|
rescue Errno::ENOENT
|
@@ -311,6 +304,7 @@ module FPM; module Fry
|
|
311
304
|
|
312
305
|
out_map.each do |output, package|
|
313
306
|
package.apply_output(output)
|
307
|
+
adjust_config_files(output)
|
314
308
|
end
|
315
309
|
|
316
310
|
out_map.each do |output, _|
|
@@ -327,6 +321,39 @@ module FPM; module Fry
|
|
327
321
|
end
|
328
322
|
|
329
323
|
|
324
|
+
def adjust_config_files( output )
|
325
|
+
# FPM flags all files in /etc as config files but only for debian :/.
|
326
|
+
# Actually this behavior makes sense to me for all packages because it's
|
327
|
+
# the thing I usually want. By setting this attribute at least the
|
328
|
+
# misleading warning goes away.
|
329
|
+
output.attributes[:deb_no_default_config_files?] = true
|
330
|
+
output.attributes[:deb_auto_config_files?] = false
|
331
|
+
|
332
|
+
return if output.attributes[:fry_config_explicitly_used]
|
333
|
+
|
334
|
+
# Now that we have disabled this for debian we have to reenable if it for
|
335
|
+
# all.
|
336
|
+
etc = File.expand_path('etc', output.staging_path)
|
337
|
+
if File.exists?( etc )
|
338
|
+
# Config plugin wasn't used. Add everything under /etc
|
339
|
+
prefix_length = output.staging_path.size + 1
|
340
|
+
added = []
|
341
|
+
Find.find(etc) do | path |
|
342
|
+
next unless File.file? path
|
343
|
+
name = path[prefix_length..-1]
|
344
|
+
if !output.config_files.include? name
|
345
|
+
added << name
|
346
|
+
output.config_files << name
|
347
|
+
end
|
348
|
+
end
|
349
|
+
if added.any?
|
350
|
+
logger.hint( "#{output.name} contains some config files in /etc. They were automatically added. You can customize this using the \"config\" plugin.",
|
351
|
+
documentation: "https://github.com/xing/fpm-fry/wiki/Plugin-config",
|
352
|
+
files: added)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
330
357
|
public
|
331
358
|
|
332
359
|
def execute
|
@@ -355,7 +382,7 @@ module FPM; module Fry
|
|
355
382
|
|
356
383
|
return 0
|
357
384
|
rescue Recipe::NotFound => e
|
358
|
-
logger.error("Recipe not found", recipe: recipe,
|
385
|
+
logger.error("Recipe not found", recipe: recipe, exception: e)
|
359
386
|
return 1
|
360
387
|
rescue => e
|
361
388
|
logger.error(e)
|
data/lib/fpm/fry/command.rb
CHANGED
@@ -16,13 +16,29 @@ module FPM; module Fry
|
|
16
16
|
|
17
17
|
subcommand 'fpm', 'Works like fpm but with docker support', FPM::Command
|
18
18
|
|
19
|
+
attr :ui
|
20
|
+
extend Forwardable
|
21
|
+
def_delegators :ui, :out, :err, :logger, :tmpdir
|
22
|
+
|
23
|
+
def initialize(invocation_path, ctx = {}, parent_attribute_values = {})
|
24
|
+
super
|
25
|
+
@ui = ctx.fetch(:ui){ UI.new }
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse(attrs)
|
29
|
+
super
|
30
|
+
if debug?
|
31
|
+
ui.logger.level = :debug
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
19
35
|
def client
|
20
36
|
@client ||= begin
|
21
37
|
client = FPM::Fry::Client.new(
|
22
38
|
logger: logger,
|
23
39
|
tls: tls?, tlsverify: tlsverify?
|
24
40
|
)
|
25
|
-
logger.
|
41
|
+
logger.debug("Docker connected",client.server_version)
|
26
42
|
client
|
27
43
|
end
|
28
44
|
end
|
@@ -39,14 +55,6 @@ module FPM; module Fry
|
|
39
55
|
extend Forwardable
|
40
56
|
def_delegators :ui, :logger
|
41
57
|
|
42
|
-
def initialize(*_)
|
43
|
-
super
|
44
|
-
@ui = UI.new()
|
45
|
-
if debug?
|
46
|
-
ui.logger.level = :debug
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
58
|
def execute
|
51
59
|
require 'fpm/fry/os_db'
|
52
60
|
require 'fpm/fry/detector'
|
data/lib/fpm/fry/detector.rb
CHANGED
@@ -73,6 +73,10 @@ module FPM; module Fry
|
|
73
73
|
end
|
74
74
|
|
75
75
|
class Image < Struct.new(:client,:image,:factory)
|
76
|
+
|
77
|
+
class ImageNotFound < StandardError
|
78
|
+
end
|
79
|
+
|
76
80
|
attr :distribution
|
77
81
|
attr :version
|
78
82
|
|
@@ -82,11 +86,15 @@ module FPM; module Fry
|
|
82
86
|
|
83
87
|
def detect!
|
84
88
|
body = JSON.generate({"Image" => image, "Cmd" => "exit 0"})
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
89
|
+
begin
|
90
|
+
res = client.post( path: client.url('containers','create'),
|
91
|
+
headers: {'Content-Type' => 'application/json'},
|
92
|
+
body: body,
|
93
|
+
expects: [201]
|
94
|
+
)
|
95
|
+
rescue Excon::Errors::NotFound
|
96
|
+
raise ImageNotFound, "Image #{image.inspect} not found. Did you do a `docker pull #{image}` before?"
|
97
|
+
end
|
90
98
|
body = JSON.parse(res.body)
|
91
99
|
container = body.fetch('Id')
|
92
100
|
begin
|
data/lib/fpm/fry/docker_file.rb
CHANGED
@@ -7,6 +7,8 @@ require 'fpm/fry/joined_io'
|
|
7
7
|
module FPM; module Fry
|
8
8
|
class DockerFile < Struct.new(:variables,:cache,:recipe)
|
9
9
|
|
10
|
+
NAME = 'Dockerfile.fpm-fry'
|
11
|
+
|
10
12
|
class Source < Struct.new(:variables, :cache)
|
11
13
|
|
12
14
|
def initialize(variables, cache = Source::Null::Cache)
|
@@ -42,7 +44,7 @@ module FPM; module Fry
|
|
42
44
|
def self_tar_io
|
43
45
|
sio = StringIO.new
|
44
46
|
tar = Gem::Package::TarWriter.new(sio)
|
45
|
-
tar.add_file(
|
47
|
+
tar.add_file(NAME,'0777') do |io|
|
46
48
|
io.write(dockerfile)
|
47
49
|
end
|
48
50
|
#tar.close
|
@@ -122,8 +124,10 @@ module FPM; module Fry
|
|
122
124
|
def build_sh
|
123
125
|
df = ['#!/bin/bash']
|
124
126
|
df << 'set -e'
|
125
|
-
recipe.steps.each do |
|
126
|
-
|
127
|
+
recipe.steps.each do |v|
|
128
|
+
if v.respond_to? :name
|
129
|
+
df << "echo -e '\\e[1;32m====> #{Shellwords.escape v.name}\\e[0m'"
|
130
|
+
end
|
127
131
|
df << v.to_s
|
128
132
|
end
|
129
133
|
df << ''
|
@@ -136,7 +140,7 @@ module FPM; module Fry
|
|
136
140
|
tar.add_file('.build.sh','0777') do |io|
|
137
141
|
io.write(build_sh)
|
138
142
|
end
|
139
|
-
tar.add_file(
|
143
|
+
tar.add_file(NAME,'0777') do |io|
|
140
144
|
io.write(dockerfile)
|
141
145
|
end
|
142
146
|
#tar.close
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'fpm/fry/plugin'
|
2
|
+
module FPM::Fry::Plugin::Config
|
3
|
+
|
4
|
+
# @api private
|
5
|
+
IMPLICIT = Module.new
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
MARK_EXPLICIT = Module.new do
|
9
|
+
def self.call(_, package)
|
10
|
+
package.attributes[:fry_config_explicitly_used] = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
class Callback < Struct.new(:files)
|
16
|
+
|
17
|
+
def call(_, package)
|
18
|
+
prefix_length = package.staging_path.size + 1
|
19
|
+
candidates = []
|
20
|
+
# Sorting is important so that more specific rules override more generic
|
21
|
+
# rules.
|
22
|
+
keys = files.keys.sort_by(&:size)
|
23
|
+
keys.each do | key |
|
24
|
+
if files[key]
|
25
|
+
# Inclusion rule. Crawl file system for candidates
|
26
|
+
Find.find( File.expand_path(key, package.staging_path) ) do | path |
|
27
|
+
next unless File.file? path
|
28
|
+
name = path[prefix_length..-1]
|
29
|
+
candidates << name
|
30
|
+
end
|
31
|
+
else
|
32
|
+
# Exclusion rule. Remove matching candidates
|
33
|
+
keydir = key + "/"
|
34
|
+
candidates.reject!{ |can|
|
35
|
+
can.start_with?(keydir) || can == key
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
package.config_files |= candidates
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class DSL < Struct.new(:builder, :options, :callback)
|
45
|
+
|
46
|
+
def initialize( builder, options )
|
47
|
+
callback = builder.output_hooks.find{|h| h.kind_of? Callback }
|
48
|
+
if !callback
|
49
|
+
callback = Callback.new({'etc' => true})
|
50
|
+
builder.output_hooks << callback
|
51
|
+
end
|
52
|
+
# This looks kind of dirty. The callback tells the cook comamnd that the
|
53
|
+
# user has explictly used the config plugin. This way the cook command
|
54
|
+
# can hint the user to use this plugin if config files were automatically
|
55
|
+
# added.
|
56
|
+
if !options[IMPLICIT]
|
57
|
+
builder.output_hooks << MARK_EXPLICIT
|
58
|
+
end
|
59
|
+
super( builder, options, callback )
|
60
|
+
end
|
61
|
+
|
62
|
+
def include( path )
|
63
|
+
if path[0] == "/"
|
64
|
+
path = path[1..-1]
|
65
|
+
end
|
66
|
+
callback.files[path] = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def exclude( path )
|
70
|
+
if path[0] == "/"
|
71
|
+
path = path[1..-1]
|
72
|
+
end
|
73
|
+
callback.files[path] = false
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.apply( builder, options = {}, &block )
|
79
|
+
dsl = DSL.new(builder, options)
|
80
|
+
if block
|
81
|
+
if block.arity == 1
|
82
|
+
yield dsl
|
83
|
+
else
|
84
|
+
dsl.instance_eval(&block)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
dsl
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module FPM::Fry::Plugin::SameVersion
|
2
|
+
|
3
|
+
# Generates a special constraint that ignores iterations.
|
4
|
+
# This is especially pointful in multi-package recipes.
|
5
|
+
# @example
|
6
|
+
# name 'mainpackage'
|
7
|
+
# version '0.2.3'
|
8
|
+
# package 'subpackage'
|
9
|
+
# plugin 'same_version'
|
10
|
+
# depends 'mainpackage', same_version
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
def same_version
|
14
|
+
*head, last = version.split('.')
|
15
|
+
last = last.to_i + 1
|
16
|
+
return ">= #{version}, << #{head.join '.'}.#{last}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -1,29 +1,39 @@
|
|
1
1
|
require 'fpm/fry/plugin'
|
2
2
|
require 'fpm/fry/plugin/init'
|
3
3
|
require 'fpm/fry/plugin/edit_staging'
|
4
|
+
require 'fpm/fry/plugin/config'
|
4
5
|
require 'erb'
|
5
6
|
require 'shellwords'
|
6
7
|
module FPM::Fry::Plugin ; module Service
|
7
8
|
|
8
|
-
class Environment < Struct.new(:name,:command, :description)
|
9
|
+
class Environment < Struct.new(:name,:command, :description, :limits, :user, :group, :chdir)
|
9
10
|
|
10
11
|
def render(file)
|
11
12
|
_erbout = ""
|
12
13
|
erb = ERB.new(
|
13
|
-
IO.read(File.join(File.dirname(__FILE__),'..','templates',file))
|
14
|
+
IO.read(File.join(File.dirname(__FILE__),'..','templates',file)),
|
15
|
+
0, "-"
|
14
16
|
)
|
15
|
-
eval(erb.src)
|
17
|
+
eval(erb.src,nil,File.join(File.dirname(__FILE__),'..','templates',file))
|
16
18
|
return _erbout
|
17
19
|
end
|
18
20
|
|
19
21
|
end
|
20
22
|
|
23
|
+
LIMITS = %w(core cpu data fsize memlock msgqueue nice nofile nproc rss rtprio sigpending stack)
|
24
|
+
|
21
25
|
class DSL
|
22
26
|
|
27
|
+
attr :limits
|
28
|
+
|
23
29
|
def initialize(*_)
|
24
30
|
super
|
25
31
|
@name = nil
|
26
32
|
@command = []
|
33
|
+
@limits = {}
|
34
|
+
@user = nil
|
35
|
+
@group = nil
|
36
|
+
@chdir = nil
|
27
37
|
end
|
28
38
|
|
29
39
|
def name( n = nil )
|
@@ -33,6 +43,34 @@ module FPM::Fry::Plugin ; module Service
|
|
33
43
|
return @name
|
34
44
|
end
|
35
45
|
|
46
|
+
def group( n = nil )
|
47
|
+
if n
|
48
|
+
@group = n
|
49
|
+
end
|
50
|
+
return @group
|
51
|
+
end
|
52
|
+
|
53
|
+
def user( n = nil )
|
54
|
+
if n
|
55
|
+
@user = n
|
56
|
+
end
|
57
|
+
return @user
|
58
|
+
end
|
59
|
+
|
60
|
+
def limit( name, soft, hard = soft )
|
61
|
+
unless LIMITS.include? name
|
62
|
+
raise ArgumentError, "Unknown limit #{name.inspect}. Known limits are: #{LIMITS.inspect}"
|
63
|
+
end
|
64
|
+
@limits[name] = [soft,hard]
|
65
|
+
end
|
66
|
+
|
67
|
+
def chdir( dir = nil )
|
68
|
+
if dir
|
69
|
+
@chdir = dir
|
70
|
+
end
|
71
|
+
@chdir
|
72
|
+
end
|
73
|
+
|
36
74
|
def command( *args )
|
37
75
|
if args.any?
|
38
76
|
@command = args
|
@@ -45,7 +83,7 @@ module FPM::Fry::Plugin ; module Service
|
|
45
83
|
name = self.name || builder.name || raise
|
46
84
|
init = Init.detect_init(builder.variables)
|
47
85
|
edit = builder.plugin('edit_staging')
|
48
|
-
env = Environment.new(name, command, "")
|
86
|
+
env = Environment.new(name, command, "", @limits, @user, @group, @chdir)
|
49
87
|
case(init)
|
50
88
|
when 'upstart' then
|
51
89
|
edit.add_file "/etc/init/#{name}.conf",StringIO.new( env.render('upstart.erb') )
|
@@ -64,6 +102,10 @@ if status #{Shellwords.shellescape name} 2>/dev/null | grep -q ' start/'; then
|
|
64
102
|
fi
|
65
103
|
BASH
|
66
104
|
end
|
105
|
+
builder.plugin('config', FPM::Fry::Plugin::Config::IMPLICIT => true) do |co|
|
106
|
+
co.include "etc/init/#{name}.conf"
|
107
|
+
co.include "etc/init.d/#{name}"
|
108
|
+
end
|
67
109
|
when 'sysv' then
|
68
110
|
edit.add_file "/etc/init.d/#{name}",StringIO.new( env.render('sysv.erb') ), chmod: '750'
|
69
111
|
builder.plugin('script_helper') do |sh|
|
@@ -76,6 +118,9 @@ BASH
|
|
76
118
|
update-rc.d -f #{Shellwords.shellescape name} remove
|
77
119
|
BASH
|
78
120
|
end
|
121
|
+
builder.plugin('config', FPM::Fry::Plugin::Config::IMPLICIT => true) do |co|
|
122
|
+
co.include "etc/init.d/#{name}"
|
123
|
+
end
|
79
124
|
when 'systemd' then
|
80
125
|
|
81
126
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'fpm/fry/plugin'
|
2
|
+
module FPM::Fry::Plugin ; module User
|
3
|
+
|
4
|
+
def self.apply(builder, name, options = {}, &block)
|
5
|
+
cmd = ["adduser", "--system"]
|
6
|
+
case options[:group]
|
7
|
+
when String
|
8
|
+
cmd << '--ingroup' << options[:group]
|
9
|
+
when true
|
10
|
+
cmd << '--group'
|
11
|
+
when nil
|
12
|
+
else
|
13
|
+
raise ArgumentError, ":group must be a String or true, got #{options[:group].inspect}"
|
14
|
+
end
|
15
|
+
cmd << name
|
16
|
+
builder.plugin('script_helper') do |sh|
|
17
|
+
sh.after_install_or_upgrade(Shellwords.shelljoin(cmd))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end ; end
|
@@ -86,6 +86,9 @@ module FPM::Fry
|
|
86
86
|
if mod.respond_to? :apply
|
87
87
|
mod.apply(self, *args, &block)
|
88
88
|
else
|
89
|
+
if args.any? or block_given?
|
90
|
+
raise ArgumentError, "Simple plugins can't accept additional arguments and blocks."
|
91
|
+
end
|
89
92
|
extend(mod)
|
90
93
|
end
|
91
94
|
end
|
@@ -131,14 +134,16 @@ module FPM::Fry
|
|
131
134
|
|
132
135
|
def parse_package( name, options = {} )
|
133
136
|
if options.kind_of? String
|
134
|
-
options = {
|
137
|
+
options = {constraints: options}
|
135
138
|
end
|
136
|
-
case(v = options[:
|
139
|
+
case(v = options[:constraints])
|
137
140
|
when String
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
141
|
+
options[:constraints] = v.split(',').map do |c|
|
142
|
+
if c =~ /\A\s*(<=|<<|>=|>>|<>|=|>|<)(\s*)/
|
143
|
+
$1 + ' ' + $'
|
144
|
+
else
|
145
|
+
'= ' + c
|
146
|
+
end
|
142
147
|
end
|
143
148
|
end
|
144
149
|
return name, options
|
@@ -202,8 +207,16 @@ module FPM::Fry
|
|
202
207
|
options = {}
|
203
208
|
end
|
204
209
|
command = args.shift
|
205
|
-
name = options.fetch(:name){ [command,*args].select{|c| c[0] != '-' }.join('
|
206
|
-
|
210
|
+
name = options.fetch(:name){ [command,*args].select{|c| c[0] != '-' }.join(' ') }
|
211
|
+
bash( name, Shellwords.join([command, *args]) )
|
212
|
+
end
|
213
|
+
|
214
|
+
def bash( name = nil, code )
|
215
|
+
if name
|
216
|
+
recipe.steps << Recipe::Step.new(name, code)
|
217
|
+
else
|
218
|
+
recipe.steps << code.to_s
|
219
|
+
end
|
207
220
|
end
|
208
221
|
|
209
222
|
def build_depends( name , options = {} )
|
data/lib/fpm/fry/recipe.rb
CHANGED
@@ -12,6 +12,12 @@ module FPM; module Fry
|
|
12
12
|
|
13
13
|
class Recipe
|
14
14
|
|
15
|
+
class Step < Struct.new(:name, :value)
|
16
|
+
def to_s
|
17
|
+
value.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
15
21
|
class PackageRecipe
|
16
22
|
attr_accessor :name,
|
17
23
|
:iteration,
|
@@ -62,7 +68,14 @@ module FPM; module Fry
|
|
62
68
|
end
|
63
69
|
[:dependencies, :conflicts, :replaces, :provides].each do |sym|
|
64
70
|
send(sym).each do |name, options|
|
65
|
-
|
71
|
+
constr = Array(options[:constraints])
|
72
|
+
if constr.any?
|
73
|
+
constr.each do | c |
|
74
|
+
package.send(sym) << "#{name} #{c}"
|
75
|
+
end
|
76
|
+
else
|
77
|
+
package.send(sym) << name
|
78
|
+
end
|
66
79
|
end
|
67
80
|
end
|
68
81
|
output_hooks.each{|h| h.call(self, package) }
|
@@ -112,7 +125,7 @@ module FPM; module Fry
|
|
112
125
|
|
113
126
|
def initialize
|
114
127
|
@source = Source::Null
|
115
|
-
@steps =
|
128
|
+
@steps = []
|
116
129
|
@packages = [PackageRecipe.new]
|
117
130
|
@packages[0].files << '**'
|
118
131
|
@build_depends = {}
|
@@ -1,4 +1,21 @@
|
|
1
1
|
#!/bin/sh
|
2
|
+
<%
|
3
|
+
startopts = ['']
|
4
|
+
if user
|
5
|
+
if group
|
6
|
+
startopts << '-c' << Shellwords.escape(user + ':' + group)
|
7
|
+
else
|
8
|
+
startopts << '-c' << Shellwords.escape(user)
|
9
|
+
end
|
10
|
+
else
|
11
|
+
if group
|
12
|
+
startopts << '-g' << Shellwords.escape(group)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
if chdir
|
16
|
+
startopts << '-d' << Shellwords.escape(chdir)
|
17
|
+
end
|
18
|
+
-%>
|
2
19
|
### BEGIN INIT INFO
|
3
20
|
# Provides: <%= name %>
|
4
21
|
# Required-Start: $remote_fs $syslog
|
@@ -42,7 +59,7 @@ do_start()
|
|
42
59
|
# 2 if daemon could not be started
|
43
60
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
44
61
|
|| return 1
|
45
|
-
start-stop-daemon --start --quiet --pidfile $PIDFILE --background --exec $DAEMON -- \
|
62
|
+
start-stop-daemon --start --quiet --pidfile $PIDFILE --background<%= startopts.join(" ") %> --exec $DAEMON -- \
|
46
63
|
$DAEMON_ARGS \
|
47
64
|
|| return 2
|
48
65
|
# Add code here, if necessary, that waits for the process to be ready
|
@@ -7,6 +7,15 @@ env NAME=<%= name.inspect %>
|
|
7
7
|
env DAEMON=<%= command[0].inspect %>
|
8
8
|
env DAEMON_ARGS=<%= Shellwords.shelljoin(command[1..-1]).inspect %>
|
9
9
|
|
10
|
+
<% if user %>setuid <%= user.inspect %>
|
11
|
+
<% end -%>
|
12
|
+
<% if group %>setgid <%= group.inspect %>
|
13
|
+
<% end -%>
|
14
|
+
<% if chdir %>chdir <%= chdir.inspect %>
|
15
|
+
<% end -%>
|
16
|
+
<% limits.each do | name, (soft, hard) | -%>
|
17
|
+
limit <%= name %> <%= soft %> <%= hard %>
|
18
|
+
<% end -%>
|
10
19
|
respawn
|
11
20
|
|
12
21
|
script
|
data/lib/fpm/fry/ui.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'cabin/nice_output'
|
2
|
+
require 'fpm/fry/channel'
|
2
3
|
module FPM; module Fry
|
3
4
|
class UI < Struct.new(:out, :err, :logger, :tmpdir)
|
4
5
|
|
5
6
|
def initialize( out = STDOUT, err = STDERR, logger = nil , tmpdir = '/tmp/fpm-fry' )
|
6
|
-
logger ||=
|
7
|
+
logger ||= Channel.new.tap{|chan| chan.subscribe(Cabin::NiceOutput.new(out)) }
|
7
8
|
FileUtils.mkdir_p( tmpdir )
|
8
9
|
super( out, err, logger, tmpdir )
|
9
10
|
end
|
data/lib/fpm/package/docker.rb
CHANGED
@@ -25,7 +25,7 @@ class FPM::Package::Docker < FPM::Package
|
|
25
25
|
def split( name, map )
|
26
26
|
changes = changes(name)
|
27
27
|
changes.remove_modified_leaves! do | ml |
|
28
|
-
@logger.warn("Found a modified file. You can only create new files in a package",
|
28
|
+
@logger.warn("Found a modified file. You can only create new files in a package",name: ml)
|
29
29
|
end
|
30
30
|
fmap = {}
|
31
31
|
changes.leaves.each do | change |
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fpm-fry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hannes Georg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: excon
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- lib/cabin/nice_output.rb
|
50
50
|
- lib/fpm/fry/block_enumerator.rb
|
51
51
|
- lib/fpm/fry/build_output_parser.rb
|
52
|
+
- lib/fpm/fry/channel.rb
|
52
53
|
- lib/fpm/fry/client.rb
|
53
54
|
- lib/fpm/fry/command.rb
|
54
55
|
- lib/fpm/fry/command/cook.rb
|
@@ -58,12 +59,15 @@ files:
|
|
58
59
|
- lib/fpm/fry/os_db.rb
|
59
60
|
- lib/fpm/fry/plugin.rb
|
60
61
|
- lib/fpm/fry/plugin/alternatives.rb
|
62
|
+
- lib/fpm/fry/plugin/config.rb
|
61
63
|
- lib/fpm/fry/plugin/edit_staging.rb
|
62
64
|
- lib/fpm/fry/plugin/exclude.rb
|
63
65
|
- lib/fpm/fry/plugin/init.rb
|
64
66
|
- lib/fpm/fry/plugin/platforms.rb
|
67
|
+
- lib/fpm/fry/plugin/same_version.rb
|
65
68
|
- lib/fpm/fry/plugin/script_helper.rb
|
66
69
|
- lib/fpm/fry/plugin/service.rb
|
70
|
+
- lib/fpm/fry/plugin/user.rb
|
67
71
|
- lib/fpm/fry/recipe.rb
|
68
72
|
- lib/fpm/fry/recipe/builder.rb
|
69
73
|
- lib/fpm/fry/source.rb
|
@@ -103,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
107
|
version: '0'
|
104
108
|
requirements: []
|
105
109
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
110
|
+
rubygems_version: 2.4.8
|
107
111
|
signing_key:
|
108
112
|
specification_version: 4
|
109
113
|
summary: FPM Fry
|