conjur-debify 3.0.0.pre.1118 → 3.0.1.pre.2
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 +4 -4
- data/.dockerignore +1 -0
- data/.gitignore +22 -0
- data/.project +18 -0
- data/.rvmrc +60 -0
- data/CHANGELOG.md +255 -0
- data/CONTRIBUTING.md +16 -0
- data/Dockerfile +33 -0
- data/Gemfile +2 -0
- data/Jenkinsfile +116 -0
- data/LICENSE.txt +22 -0
- data/README.md +303 -0
- data/Rakefile +75 -0
- data/VERSION +1 -1
- data/bin/debify +5 -0
- data/build.sh +8 -0
- data/ci/test.sh +10 -0
- data/debify.gemspec +36 -0
- data/distrib/conjur_creds.rb +7 -0
- data/distrib/docker-debify +50 -0
- data/distrib/entrypoint.sh +23 -0
- data/distrib/script +1 -0
- data/distrib/secrets +1 -0
- data/distrib/secrets.yml +2 -0
- data/example/Gemfile +9 -0
- data/example/Gemfile.lock +32 -0
- data/example/debify.sh +3 -0
- data/example/distrib/postinstall.sh +8 -0
- data/example/docker-compose.yml +11 -0
- data/example/net-test.sh +7 -0
- data/example/test.sh +4 -0
- data/features/detect_version.feature +12 -0
- data/features/package.feature +23 -0
- data/features/sandbox.feature +23 -0
- data/features/step_definitions/debify_steps.rb +29 -0
- data/features/support/env.rb +12 -0
- data/features/support/hooks.rb +29 -0
- data/features/support/world.rb +10 -0
- data/features/test.feature +24 -0
- data/image-tags +23 -0
- data/lib/conjur/debify/Dockerfile.fpm +13 -0
- data/lib/conjur/debify/action/publish.rb +136 -0
- data/lib/conjur/debify/utils.rb +16 -0
- data/lib/conjur/debify/version.rb +5 -0
- data/lib/conjur/debify.rb +851 -0
- data/lib/conjur/fpm/Dockerfile +26 -0
- data/lib/conjur/fpm/debify_utils.sh +32 -0
- data/lib/conjur/fpm/package.sh +109 -0
- data/lib/conjur/publish/Dockerfile +5 -0
- data/publish-rubygem.sh +12 -0
- data/push-image.sh +6 -0
- data/secrets.yml +3 -0
- data/spec/action/publish_spec.rb +54 -0
- data/spec/data/Makefile +5 -0
- data/spec/data/test.tar +0 -0
- data/spec/debify_utils_spec.rb +55 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/utils_spec.rb +22 -0
- data/tag-image.sh +6 -0
- data/test.sh +6 -0
- metadata +77 -4
@@ -0,0 +1,851 @@
|
|
1
|
+
require "conjur/debify/version"
|
2
|
+
require 'docker'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'gli'
|
5
|
+
require 'json'
|
6
|
+
require 'base64'
|
7
|
+
require 'tmpdir'
|
8
|
+
|
9
|
+
require 'conjur/debify/utils'
|
10
|
+
|
11
|
+
require 'active_support'
|
12
|
+
require 'active_support/core_ext'
|
13
|
+
|
14
|
+
include GLI::App
|
15
|
+
|
16
|
+
DEFAULT_FILE_TYPE = "deb"
|
17
|
+
|
18
|
+
config_file '.debifyrc'
|
19
|
+
|
20
|
+
desc 'Set an environment variable (e.g. TERM=xterm) when starting a container'
|
21
|
+
flag [:env], :multiple => true
|
22
|
+
|
23
|
+
desc 'Mount local bundle to reuse gems from previous installation'
|
24
|
+
default_value true
|
25
|
+
switch [:'local-bundle']
|
26
|
+
|
27
|
+
|
28
|
+
Docker.options[:read_timeout] = 300
|
29
|
+
|
30
|
+
# This is used to turn on DEBUG notices.
|
31
|
+
module DebugMixin
|
32
|
+
DEBUG = ENV['DEBUG'].nil? ? true : ENV['DEBUG'].downcase == 'true'
|
33
|
+
|
34
|
+
def debug *a
|
35
|
+
DebugMixin.debug *a
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.debug *a
|
39
|
+
$stderr.puts *a if DEBUG
|
40
|
+
end
|
41
|
+
|
42
|
+
def debug_write *a
|
43
|
+
DebugMixin.debug_write *a
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.debug_write *a
|
47
|
+
$stderr.write *a if DEBUG
|
48
|
+
end
|
49
|
+
|
50
|
+
# you can give this to various docker methods to print output if debug is on
|
51
|
+
def self.docker_debug *a
|
52
|
+
if a.length == 2 && a[0].is_a?(Symbol)
|
53
|
+
debug a.last
|
54
|
+
else
|
55
|
+
a.each do |line|
|
56
|
+
begin
|
57
|
+
line = JSON.parse(line)
|
58
|
+
line.keys.each do |k|
|
59
|
+
debug line[k]
|
60
|
+
end
|
61
|
+
rescue JSON::ParserError
|
62
|
+
# Docker For Mac is spitting out invalid JSON, so just print
|
63
|
+
# out the line if parsing fails.
|
64
|
+
debug line
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
DOCKER = method :docker_debug
|
71
|
+
end
|
72
|
+
|
73
|
+
program_desc 'Utility commands for building and testing Conjur appliance Debian packages'
|
74
|
+
|
75
|
+
version Conjur::Debify::VERSION
|
76
|
+
|
77
|
+
subcommand_option_handling :normal
|
78
|
+
arguments :strict
|
79
|
+
|
80
|
+
def detect_version
|
81
|
+
if File.exists?("VERSION") && !(base_commit = `git log --pretty='%h' VERSION | head -n 1`.strip).empty?
|
82
|
+
base_version = File.read("VERSION").strip
|
83
|
+
commits_since = `git log #{base_commit}..HEAD --pretty='%h'`.split("\n").size
|
84
|
+
hash = `git rev-parse --short HEAD`.strip
|
85
|
+
[[base_version, commits_since].join('.'), hash].join("-")
|
86
|
+
else
|
87
|
+
`git describe --long --tags --abbrev=7 --match 'v*.*.*' | sed -e 's/^v//'`.strip.tap do |version|
|
88
|
+
raise "No Git version (tag) for project" if version.empty?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def git_files
|
94
|
+
files = (`git ls-files -z`.split("\x0") + ['Gemfile.lock', 'VERSION']).uniq
|
95
|
+
# Since submodule directories are listed, but are not files, we remove them.
|
96
|
+
# Currently, `conjur-project-config` is the only submodule in Conjur, and it
|
97
|
+
# can safely be removed because it's a developer-only tool. If we add another
|
98
|
+
# submodule in the future needed for production, we'll need to update this
|
99
|
+
# code. But YAGNI for now.
|
100
|
+
files.select { |f| File.file?(f) }
|
101
|
+
end
|
102
|
+
|
103
|
+
def login_to_registry(appliance_image_id)
|
104
|
+
config_file = File.expand_path('~/.docker/config.json')
|
105
|
+
if File.exist? config_file
|
106
|
+
json_config = JSON.parse(File.read(config_file))
|
107
|
+
registry = appliance_image_id.split('/')[0]
|
108
|
+
|
109
|
+
json_auth = json_config['auths'][registry]['auth']
|
110
|
+
if json_auth
|
111
|
+
username, password = Base64.decode64(json_auth).split(':')
|
112
|
+
Docker.authenticate! username: username, password: password, serveraddress: registry
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
desc "Clean current working directory of non-Git-managed files"
|
118
|
+
long_desc <<DESC
|
119
|
+
Reliable builds depend on having a clean working directory.
|
120
|
+
|
121
|
+
Because debify runs some commands in volume-mounted Docker containers,
|
122
|
+
it is capable of creating root-owned files.
|
123
|
+
|
124
|
+
This command will delete all files in the working directory that are not
|
125
|
+
git-managed. The command is designed to run in Jenkins. Therefore, it will
|
126
|
+
only perform file deletion if:
|
127
|
+
|
128
|
+
* The current user, as provided by Etc.getlogin, is 'jenkins'
|
129
|
+
* The BUILD_NUMBER environment variable is set
|
130
|
+
|
131
|
+
File deletion can be compelled using the "force" option.
|
132
|
+
DESC
|
133
|
+
arg_name "project-name -- <fpm-arguments>"
|
134
|
+
command "clean" do |c|
|
135
|
+
c.desc "Set the current working directory"
|
136
|
+
c.flag [:d, "dir"]
|
137
|
+
|
138
|
+
c.desc "Ignore (don't delete) a file or directory"
|
139
|
+
c.flag [:i, :ignore]
|
140
|
+
|
141
|
+
c.desc "Force file deletion even if if this doesn't look like a Jenkins environment"
|
142
|
+
c.switch [:force]
|
143
|
+
|
144
|
+
c.action do |global_options, cmd_options, args|
|
145
|
+
def looks_like_jenkins?
|
146
|
+
require 'etc'
|
147
|
+
Etc.getlogin == 'jenkins' && ENV['BUILD_NUMBER']
|
148
|
+
end
|
149
|
+
|
150
|
+
require 'set'
|
151
|
+
perform_deletion = cmd_options[:force] || looks_like_jenkins?
|
152
|
+
if !perform_deletion
|
153
|
+
$stderr.puts "No --force, and this doesn't look like Jenkins. I won't actually delete anything"
|
154
|
+
end
|
155
|
+
@ignore_list = Array(cmd_options[:ignore]) + ['.', '..', '.git']
|
156
|
+
|
157
|
+
def ignore_file? f
|
158
|
+
@ignore_list.find { |ignore| f.index(ignore) == 0 }
|
159
|
+
end
|
160
|
+
|
161
|
+
dir = cmd_options[:dir] || '.'
|
162
|
+
dir = File.expand_path(dir)
|
163
|
+
Dir.chdir dir do
|
164
|
+
require 'find'
|
165
|
+
find_files = []
|
166
|
+
Find.find('.').each do |p|
|
167
|
+
find_files.push p[2..-1]
|
168
|
+
end
|
169
|
+
find_files.compact!
|
170
|
+
delete_files = (find_files - git_files)
|
171
|
+
delete_files.delete_if { |file|
|
172
|
+
File.directory?(file) || ignore_file?(file)
|
173
|
+
}
|
174
|
+
if perform_deletion
|
175
|
+
image = Docker::Image.create 'fromImage' => "alpine:3.3"
|
176
|
+
options = {
|
177
|
+
'Cmd' => ["sh", "-c", "while true; do sleep 1; done"],
|
178
|
+
'Image' => image.id,
|
179
|
+
'Binds' => [
|
180
|
+
[dir, "/src"].join(':'),
|
181
|
+
]
|
182
|
+
}
|
183
|
+
options['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
184
|
+
container = Docker::Container.create options
|
185
|
+
begin
|
186
|
+
container.start!
|
187
|
+
delete_files.each do |file|
|
188
|
+
puts file
|
189
|
+
|
190
|
+
file = "/src/#{file}"
|
191
|
+
cmd = ["rm", "-f", file]
|
192
|
+
|
193
|
+
stdout, stderr, status = container.exec cmd, &DebugMixin::DOCKER
|
194
|
+
$stderr.puts "Failed to delete #{file}" unless status == 0
|
195
|
+
end
|
196
|
+
ensure
|
197
|
+
container.delete force: true
|
198
|
+
end
|
199
|
+
else
|
200
|
+
delete_files.each do |file|
|
201
|
+
puts file
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def copy_packages_from_container(container, package_name, dev_package_name)
|
209
|
+
Conjur::Debify::Utils.copy_from_container container, "/src/#{package_name}"
|
210
|
+
puts "#{package_name}"
|
211
|
+
begin
|
212
|
+
Conjur::Debify::Utils.copy_from_container container, "/dev-pkg/#{dev_package_name}"
|
213
|
+
puts "#{dev_package_name}"
|
214
|
+
rescue Docker::Error::NotFoundError
|
215
|
+
warn "#{dev_package_name} not found. The package might not have any development dependencies."
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
desc "Build a debian package for a project"
|
220
|
+
long_desc <<DESC
|
221
|
+
The package is built using fpm (https://github.com/jordansissel/fpm).
|
222
|
+
|
223
|
+
The project directory is required to contain:
|
224
|
+
|
225
|
+
* A Gemfile and Gemfile.lock
|
226
|
+
* A shell script called debify.sh
|
227
|
+
|
228
|
+
debify.sh is invoked by the package build process to create any custom
|
229
|
+
files, other than the project source tree. For example, config files can be
|
230
|
+
created in /opt/conjur/etc.
|
231
|
+
|
232
|
+
The distrib folder in the project source tree is intended to create scripts
|
233
|
+
for package pre-install, post-install etc. The distrib folder is not included
|
234
|
+
in the deb package, so its contents should be copied to the file system or
|
235
|
+
packaged using fpm arguments.
|
236
|
+
|
237
|
+
All arguments to this command which follow the double-dash are propagated to
|
238
|
+
the fpm command.
|
239
|
+
DESC
|
240
|
+
arg_name "project-name -- <fpm-arguments>"
|
241
|
+
command "package" do |c|
|
242
|
+
c.desc "Set the current working directory"
|
243
|
+
c.flag [:d, "dir"]
|
244
|
+
|
245
|
+
c.desc "Set the output file type of the fpm command (e.g rpm)"
|
246
|
+
c.flag [:o, :output]
|
247
|
+
|
248
|
+
c.desc "Specify the deb version; by default, it's read from the VERSION file"
|
249
|
+
c.flag [:v, :version]
|
250
|
+
|
251
|
+
c.desc "Specify a custom Dockerfile.fpm"
|
252
|
+
c.flag [:dockerfile]
|
253
|
+
|
254
|
+
c.desc "Specify files to add to the FPM image that are not included from the git repo"
|
255
|
+
c.flag [:'additional-files']
|
256
|
+
|
257
|
+
c.action do |global_options, cmd_options, args|
|
258
|
+
raise "project-name is required" unless project_name = args.shift
|
259
|
+
|
260
|
+
fpm_args = []
|
261
|
+
if (delimeter = args.shift) == '--'
|
262
|
+
fpm_args = args.dup
|
263
|
+
else
|
264
|
+
raise "Unexpected argument '#{delimeter}'"
|
265
|
+
end
|
266
|
+
|
267
|
+
dir = cmd_options[:dir] || '.'
|
268
|
+
pwd = File.dirname(__FILE__)
|
269
|
+
|
270
|
+
additional_files = []
|
271
|
+
if cmd_options[:'additional-files']
|
272
|
+
additional_files = cmd_options[:'additional-files'].split(',').map(&:strip)
|
273
|
+
end
|
274
|
+
|
275
|
+
begin
|
276
|
+
tries ||= 2
|
277
|
+
fpm_image = Docker::Image.build_from_dir File.expand_path('fpm', File.dirname(__FILE__)), tag: "debify-fpm", &DebugMixin::DOCKER
|
278
|
+
rescue
|
279
|
+
image_id = File.readlines(File.expand_path('fpm/Dockerfile', File.dirname(__FILE__)))
|
280
|
+
.find { | line | line =~ /^FROM/ }
|
281
|
+
.split(' ')
|
282
|
+
.last
|
283
|
+
login_to_registry image_id
|
284
|
+
retry unless (tries -= 1).zero?
|
285
|
+
end
|
286
|
+
DebugMixin.debug_write "Built base fpm image '#{fpm_image.id}'\n"
|
287
|
+
dir = File.expand_path(dir)
|
288
|
+
|
289
|
+
Dir.chdir dir do
|
290
|
+
version = cmd_options[:version] || detect_version
|
291
|
+
|
292
|
+
# move git files and Dockerfile to temp dir to make deb from
|
293
|
+
# we do this to avoid adding "non-git" files
|
294
|
+
# that aren't mentioned in the dockerignore to the deb
|
295
|
+
temp_dir = Dir.mktmpdir
|
296
|
+
DebugMixin.debug_write "Copying git files to tmp dir '#{temp_dir}'\n"
|
297
|
+
(git_files + additional_files).each do |fname|
|
298
|
+
original_file = File.join(dir, fname)
|
299
|
+
destination_path = File.join(temp_dir, fname)
|
300
|
+
FileUtils.mkdir_p(File.dirname(destination_path))
|
301
|
+
FileUtils.cp(original_file, destination_path)
|
302
|
+
end
|
303
|
+
|
304
|
+
# rename specified dockerfile to 'Dockerfile' during copy, incase name is different
|
305
|
+
dockerfile_path = cmd_options[:dockerfile] || File.expand_path("debify/Dockerfile.fpm", pwd)
|
306
|
+
temp_dockerfile = File.join(temp_dir, "Dockerfile")
|
307
|
+
|
308
|
+
# change image variable in specified Dockerfile
|
309
|
+
dockerfile = File.read(dockerfile_path)
|
310
|
+
replace_image = dockerfile.gsub("@@image@@", fpm_image.id)
|
311
|
+
File.open(temp_dockerfile, "w") { |file| file.puts replace_image }
|
312
|
+
|
313
|
+
# build image from project being debified dir
|
314
|
+
image = Docker::Image.build_from_dir temp_dir, &DebugMixin::DOCKER
|
315
|
+
|
316
|
+
DebugMixin.debug_write "Built fpm image '#{image.id}' for project #{project_name}\n"
|
317
|
+
|
318
|
+
container_cmd_options = [project_name, version]
|
319
|
+
|
320
|
+
# Set the output file type if present
|
321
|
+
file_type = cmd_options[:output] || DEFAULT_FILE_TYPE
|
322
|
+
container_cmd_options << "--file-type=#{file_type}"
|
323
|
+
|
324
|
+
options = {
|
325
|
+
'Cmd' => container_cmd_options + fpm_args,
|
326
|
+
'Image' => image.id
|
327
|
+
}
|
328
|
+
options['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
329
|
+
|
330
|
+
container = Docker::Container.create options
|
331
|
+
begin
|
332
|
+
DebugMixin.debug_write "Packaging #{project_name} in container #{container.id}\n"
|
333
|
+
container.tap(&:start!).streaming_logs(follow: true, stdout: true, stderr: true) { |stream, chunk| $stderr.puts "#{chunk}" }
|
334
|
+
status = container.wait
|
335
|
+
raise "Failed to package #{project_name}" unless status['StatusCode'] == 0
|
336
|
+
|
337
|
+
if file_type == "deb"
|
338
|
+
# Copy deb packages
|
339
|
+
copy_packages_from_container(
|
340
|
+
container,
|
341
|
+
"conjur-#{project_name}_#{version}_amd64.deb",
|
342
|
+
"conjur-#{project_name}-dev_#{version}_amd64.deb"
|
343
|
+
)
|
344
|
+
elsif file_type == "rpm"
|
345
|
+
# Copy rpm packages
|
346
|
+
# The rpm builder replaces dashes with underscores in the version
|
347
|
+
rpm_version = version.tr('-', '_')
|
348
|
+
copy_packages_from_container(
|
349
|
+
container,
|
350
|
+
"conjur-#{project_name}-#{rpm_version}-1.x86_64.rpm",
|
351
|
+
"conjur-#{project_name}-dev-#{rpm_version}-1.x86_64.rpm"
|
352
|
+
)
|
353
|
+
end
|
354
|
+
ensure
|
355
|
+
container.delete(force: true)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def container_command container, *args
|
362
|
+
stdout, stderr, exitcode = container.exec args, &DebugMixin::DOCKER
|
363
|
+
exit_now! "Command failed : #{args.join(' ')}", exitcode unless exitcode == 0
|
364
|
+
stdout
|
365
|
+
end
|
366
|
+
|
367
|
+
def wait_for_conjur appliance_image, container
|
368
|
+
container_command container, '/opt/conjur/evoke/bin/wait_for_conjur'
|
369
|
+
rescue
|
370
|
+
$stderr.puts container.logs
|
371
|
+
raise
|
372
|
+
end
|
373
|
+
|
374
|
+
def network_options(cmd)
|
375
|
+
cmd.desc "Specify link for test container"
|
376
|
+
cmd.flag [:l, :link], :multiple => true
|
377
|
+
|
378
|
+
cmd.desc 'Attach to the specified network'
|
379
|
+
cmd.flag [:n, :net]
|
380
|
+
end
|
381
|
+
|
382
|
+
def short_id(id)
|
383
|
+
if id =~ /\A[0-9a-f]{64}\z/ # 64 hex digits, docker only allows lower case letters in ids
|
384
|
+
$stderr.puts "Warning: found full container id, using short id instead (#{id[0..11]} for #{id})"
|
385
|
+
id[0..11]
|
386
|
+
else
|
387
|
+
id
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# If the source of the link is a full container id, use the short id
|
392
|
+
# instead. (Docker doesn't add full container ids as network aliases,
|
393
|
+
# only short ids).
|
394
|
+
def shorten_source_id(link)
|
395
|
+
src, dest = link.split(':')
|
396
|
+
src && dest ? "#{short_id(src)}:#{dest}" : link
|
397
|
+
end
|
398
|
+
|
399
|
+
def add_network_config(container_config, cmd_options)
|
400
|
+
host_config = container_config['HostConfig']
|
401
|
+
has_links = cmd_options[:link] && !cmd_options[:link].empty?
|
402
|
+
net_name = cmd_options[:net]
|
403
|
+
if net_name
|
404
|
+
host_config['NetworkMode'] = net_name
|
405
|
+
if has_links
|
406
|
+
container_config['NetworkingConfig'] ||= {}
|
407
|
+
container_config['NetworkingConfig'].deep_merge!(
|
408
|
+
'EndpointsConfig' => {
|
409
|
+
net_name => {
|
410
|
+
'Links' => cmd_options[:link].collect(&method(:shorten_source_id))
|
411
|
+
}
|
412
|
+
}
|
413
|
+
)
|
414
|
+
end
|
415
|
+
elsif has_links
|
416
|
+
# Don't shorten source ids here
|
417
|
+
host_config['Links'] = cmd_options[:link]
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
desc "Test a Conjur debian package in a Conjur appliance container"
|
422
|
+
long_desc <<DESC
|
423
|
+
First, a Conjur appliance container is created and started. By default, the
|
424
|
+
container image is registry.tld/conjur-appliance-cuke-master. An image tag
|
425
|
+
MUST be supplied. This image is configured with all the CONJUR_ environment
|
426
|
+
variables setup for the local environment (appliance URL, cert path, admin username and
|
427
|
+
password, etc). The project source tree is also mounted into the container, at
|
428
|
+
/src/<project-name>.
|
429
|
+
|
430
|
+
This command then waits for Conjur to initialize and be healthy. It proceeds by
|
431
|
+
installing the conjur-<project-name>_<version>_amd64.deb from the project working directory.
|
432
|
+
|
433
|
+
Then the evoke "test-install" command is used to install the test code in the
|
434
|
+
/src/<project-name>. Basically, the development bundle is installed and the database
|
435
|
+
configuration (if any) is setup.
|
436
|
+
|
437
|
+
Finally, a test script from the project source tree is run, again with the container
|
438
|
+
id as the program argument.
|
439
|
+
|
440
|
+
Then the Conjur container is deleted (use --keep to leave it running).
|
441
|
+
DESC
|
442
|
+
arg_name "project-name test-script"
|
443
|
+
command "test" do |c|
|
444
|
+
c.desc "Set the current working directory"
|
445
|
+
c.flag [:d, :dir]
|
446
|
+
|
447
|
+
c.desc "Keep the Conjur appliance container after the command finishes"
|
448
|
+
c.default_value false
|
449
|
+
c.switch [:k, :keep]
|
450
|
+
|
451
|
+
c.desc "Image name"
|
452
|
+
c.default_value "registry.tld/conjur-appliance-cuke-master"
|
453
|
+
c.flag [:i, :image]
|
454
|
+
|
455
|
+
c.desc "Image tag, e.g. 4.5-stable, 4.6-stable"
|
456
|
+
c.flag [:t, "image-tag"]
|
457
|
+
|
458
|
+
c.desc "'docker pull' the Conjur container image"
|
459
|
+
c.default_value true
|
460
|
+
c.switch [:pull]
|
461
|
+
|
462
|
+
c.desc "Specify the deb version; by default, it's read from the VERSION file"
|
463
|
+
c.flag [:v, :version]
|
464
|
+
|
465
|
+
c.desc "Specify volume for test container"
|
466
|
+
c.flag [:'volumes-from'], :multiple => true
|
467
|
+
|
468
|
+
network_options(c)
|
469
|
+
|
470
|
+
c.action do |global_options, cmd_options, args|
|
471
|
+
raise "project-name is required" unless project_name = args.shift
|
472
|
+
raise "test-script is required" unless test_script = args.shift
|
473
|
+
raise "Received extra command-line arguments" if args.shift
|
474
|
+
|
475
|
+
dir = cmd_options[:dir] || '.'
|
476
|
+
dir = File.expand_path(dir)
|
477
|
+
|
478
|
+
raise "Directory #{dir} does not exist or is not a directory" unless File.directory?(dir)
|
479
|
+
raise "Directory #{dir} does not contain a .deb file" unless Dir["#{dir}/*.deb"].length >= 1
|
480
|
+
|
481
|
+
Dir.chdir dir do
|
482
|
+
image_tag = cmd_options["image-tag"] or raise "image-tag is required"
|
483
|
+
appliance_image_id = [cmd_options[:image], image_tag].join(":")
|
484
|
+
version = cmd_options[:version] || detect_version
|
485
|
+
package_name = "conjur-#{project_name}_#{version}_amd64.deb"
|
486
|
+
dev_package_name = "conjur-#{project_name}-dev_#{version}_amd64.deb"
|
487
|
+
|
488
|
+
raise "#{test_script} does not exist or is not a file" unless File.file?(test_script)
|
489
|
+
|
490
|
+
begin
|
491
|
+
tries ||= 2
|
492
|
+
Docker::Image.create 'fromImage' => appliance_image_id, &DebugMixin::DOCKER if cmd_options[:pull]
|
493
|
+
rescue
|
494
|
+
login_to_registry appliance_image_id
|
495
|
+
retry unless (tries -= 1).zero?
|
496
|
+
end
|
497
|
+
|
498
|
+
|
499
|
+
def build_test_image(appliance_image_id, project_name, packages)
|
500
|
+
packages = packages.join " "
|
501
|
+
dockerfile = <<-DOCKERFILE
|
502
|
+
FROM #{appliance_image_id}
|
503
|
+
|
504
|
+
COPY #{packages} /tmp/
|
505
|
+
|
506
|
+
RUN if dpkg --list | grep conjur-#{project_name}; then dpkg --force all --purge conjur-#{project_name}; fi
|
507
|
+
RUN if [ -f /opt/conjur/etc/#{project_name}.conf ]; then rm /opt/conjur/etc/#{project_name}.conf; fi
|
508
|
+
RUN cd /tmp; dpkg --install #{packages}
|
509
|
+
|
510
|
+
RUN touch /etc/service/conjur/down
|
511
|
+
DOCKERFILE
|
512
|
+
Dir.mktmpdir do |tmpdir|
|
513
|
+
tmpfile = Tempfile.new('Dockerfile', tmpdir)
|
514
|
+
File.write(tmpfile, dockerfile)
|
515
|
+
dockerfile_name = File.basename(tmpfile.path)
|
516
|
+
tar_cmd = "tar -cvzh -C #{tmpdir} #{dockerfile_name} -C #{Dir.pwd} #{packages}"
|
517
|
+
tar = open("| #{tar_cmd}")
|
518
|
+
begin
|
519
|
+
Docker::Image.build_from_tar(tar, :dockerfile => dockerfile_name, &DebugMixin::DOCKER)
|
520
|
+
ensure
|
521
|
+
tar.close
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
packages = [package_name]
|
527
|
+
packages << dev_package_name if File.exist? dev_package_name
|
528
|
+
|
529
|
+
begin
|
530
|
+
tries ||= 2
|
531
|
+
appliance_image = build_test_image(appliance_image_id, project_name, packages)
|
532
|
+
rescue
|
533
|
+
login_to_registry appliance_image_id
|
534
|
+
retry unless (tries -= 1).zero?
|
535
|
+
end
|
536
|
+
|
537
|
+
vendor_dir = File.expand_path("tmp/debify/#{project_name}/vendor", ENV['HOME'])
|
538
|
+
dot_bundle_dir = File.expand_path("tmp/debify/#{project_name}/.bundle", ENV['HOME'])
|
539
|
+
FileUtils.mkdir_p vendor_dir
|
540
|
+
FileUtils.mkdir_p dot_bundle_dir
|
541
|
+
options = {
|
542
|
+
'Image' => appliance_image.id,
|
543
|
+
'name' => project_name,
|
544
|
+
'Env' => [
|
545
|
+
"CONJUR_AUTHN_LOGIN=admin",
|
546
|
+
"CONJUR_ENV=appliance",
|
547
|
+
"CONJUR_AUTHN_API_KEY=SEcret12!!!!",
|
548
|
+
"CONJUR_ADMIN_PASSWORD=SEcret12!!!!",
|
549
|
+
] + global_options[:env],
|
550
|
+
'HostConfig' => {
|
551
|
+
'Binds' => [
|
552
|
+
[dir, "/src/#{project_name}"].join(':')
|
553
|
+
]
|
554
|
+
}
|
555
|
+
}
|
556
|
+
host_config = options['HostConfig']
|
557
|
+
|
558
|
+
host_config['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
559
|
+
host_config['VolumesFrom'] = cmd_options[:'volumes-from'] if cmd_options[:'volumes-from'] && !cmd_options[:'volumes-from'].empty?
|
560
|
+
|
561
|
+
add_network_config(options, cmd_options)
|
562
|
+
|
563
|
+
if global_options[:'local-bundle']
|
564
|
+
host_config['Binds']
|
565
|
+
.push([vendor_dir, "/src/#{project_name}/vendor"].join(':'))
|
566
|
+
.push([dot_bundle_dir, "/src/#{project_name}/.bundle"].join(':'))
|
567
|
+
end
|
568
|
+
|
569
|
+
container = Docker::Container.create(options.tap { |o| DebugMixin.debug_write "creating container with options #{o.inspect}" })
|
570
|
+
|
571
|
+
begin
|
572
|
+
DebugMixin.debug_write "Testing #{project_name} in container #{container.id}\n"
|
573
|
+
|
574
|
+
spawn("docker logs -f #{container.id}", [:out, :err] => $stderr).tap do |pid|
|
575
|
+
Process.detach pid
|
576
|
+
end
|
577
|
+
container.start!
|
578
|
+
|
579
|
+
# Wait for pg/main so that migrations can run
|
580
|
+
30.times do
|
581
|
+
stdout, stderr, exitcode = container.exec %w(sv status pg/main), &DebugMixin::DOCKER
|
582
|
+
status = stdout.join
|
583
|
+
break if exitcode == 0 && status =~ /^run\:/
|
584
|
+
sleep 1
|
585
|
+
end
|
586
|
+
|
587
|
+
# If we're not using shared gems, run dev-install instead of
|
588
|
+
# test-install. Even having to reinstall all the gems is
|
589
|
+
# better than dealing with Docker For Mac's current file
|
590
|
+
# sharing performance.
|
591
|
+
install_cmd = global_options[:'local-bundle'] ? 'test-install' : 'dev-install'
|
592
|
+
container_command container, "/opt/conjur/evoke/bin/#{install_cmd}", project_name
|
593
|
+
|
594
|
+
DebugMixin.debug_write "Starting conjur\n"
|
595
|
+
|
596
|
+
container_command container, "rm", "/etc/service/conjur/down"
|
597
|
+
container_command container, "sv", "start", "conjur"
|
598
|
+
wait_for_conjur appliance_image, container
|
599
|
+
|
600
|
+
system "./#{test_script} #{container.id}"
|
601
|
+
exit_now! "#{test_script} failed with exit code #{$?.exitstatus}", $?.exitstatus unless $?.exitstatus == 0
|
602
|
+
ensure
|
603
|
+
unless cmd_options[:keep] || ENV['KEEP_CONTAINERS']
|
604
|
+
DebugMixin.debug_write "deleting container"
|
605
|
+
container.delete(force: true)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
desc "Setup a development sandbox for a Conjur debian package in a Conjur appliance container"
|
613
|
+
long_desc <<DESC
|
614
|
+
First, a Conjur appliance container is created and started. By default, the
|
615
|
+
container image is registry.tld/conjur-appliance-cuke-master. An image tag
|
616
|
+
MUST be supplied. This image is configured with all the CONJUR_ environment
|
617
|
+
variables setup for the local environment (appliance URL, cert path, admin username and
|
618
|
+
password, etc). The project source tree is also mounted into the container, at
|
619
|
+
/src/<project-name>, where <project-name> is taken from the name of the current working directory.
|
620
|
+
|
621
|
+
Once in the container, use "/opt/conjur/evoke/bin/dev-install" to install the development bundle of your project.
|
622
|
+
DESC
|
623
|
+
command "sandbox" do |c|
|
624
|
+
c.desc "Set the current working directory"
|
625
|
+
c.flag [:d, :dir]
|
626
|
+
|
627
|
+
c.desc "Image name"
|
628
|
+
c.default_value "registry.tld/conjur-appliance-cuke-master"
|
629
|
+
c.flag [:i, :image]
|
630
|
+
|
631
|
+
c.desc "Image tag, e.g. 4.5-stable, 4.6-stable"
|
632
|
+
c.flag [:t, "image-tag"]
|
633
|
+
|
634
|
+
c.desc "Bind another source directory into the container. Use <src>:<dest>, where both are full paths."
|
635
|
+
c.flag [:"bind"], :multiple => true
|
636
|
+
|
637
|
+
c.desc "'docker pull' the Conjur container image"
|
638
|
+
c.default_value false
|
639
|
+
c.switch [:pull]
|
640
|
+
|
641
|
+
network_options(c)
|
642
|
+
|
643
|
+
c.desc "Specify volume for container"
|
644
|
+
c.flag [:'volumes-from'], :multiple => true
|
645
|
+
|
646
|
+
c.desc "Expose a port from the container to host. Use <host>:<container>."
|
647
|
+
c.flag [:p, :port], :multiple => true
|
648
|
+
|
649
|
+
c.desc 'Run dev-install in /src/<project-name>'
|
650
|
+
c.default_value false
|
651
|
+
c.switch [:'dev-install']
|
652
|
+
|
653
|
+
c.desc 'Kill previous sandbox container'
|
654
|
+
c.default_value false
|
655
|
+
c.switch [:kill]
|
656
|
+
|
657
|
+
c.desc 'A command to run in the sandbox'
|
658
|
+
c.flag [:c, :command]
|
659
|
+
|
660
|
+
c.action do |global_options, cmd_options, args|
|
661
|
+
raise "Received extra command-line arguments" if args.shift
|
662
|
+
|
663
|
+
dir = cmd_options[:dir] || '.'
|
664
|
+
dir = File.expand_path(dir)
|
665
|
+
|
666
|
+
raise "Directory #{dir} does not exist or is not a directory" unless File.directory?(dir)
|
667
|
+
|
668
|
+
Dir.chdir dir do
|
669
|
+
image_tag = cmd_options["image-tag"] or raise "image-tag is required"
|
670
|
+
appliance_image_id = [cmd_options[:image], image_tag].join(":")
|
671
|
+
|
672
|
+
appliance_image = if cmd_options[:pull]
|
673
|
+
begin
|
674
|
+
tries ||= 2
|
675
|
+
Docker::Image.create 'fromImage' => appliance_image_id, &DebugMixin::DOCKER if cmd_options[:pull]
|
676
|
+
rescue
|
677
|
+
login_to_registry appliance_image_id
|
678
|
+
retry unless (tries -= 1).zero?
|
679
|
+
end
|
680
|
+
else
|
681
|
+
Docker::Image.get appliance_image_id
|
682
|
+
end
|
683
|
+
|
684
|
+
project_name = File.basename(Dir.getwd)
|
685
|
+
vendor_dir = File.expand_path("tmp/debify/#{project_name}/vendor", ENV['HOME'])
|
686
|
+
dot_bundle_dir = File.expand_path("tmp/debify/#{project_name}/.bundle", ENV['HOME'])
|
687
|
+
FileUtils.mkdir_p vendor_dir
|
688
|
+
FileUtils.mkdir_p dot_bundle_dir
|
689
|
+
|
690
|
+
options = {
|
691
|
+
'name' => "#{project_name}-sandbox",
|
692
|
+
'Image' => appliance_image.id,
|
693
|
+
'WorkingDir' => "/src/#{project_name}",
|
694
|
+
'Env' => [
|
695
|
+
"CONJUR_AUTHN_LOGIN=admin",
|
696
|
+
"CONJUR_ENV=appliance",
|
697
|
+
"CONJUR_AUTHN_API_KEY=SEcret12!!!!",
|
698
|
+
"CONJUR_ADMIN_PASSWORD=SEcret12!!!!",
|
699
|
+
] + global_options[:env]
|
700
|
+
}
|
701
|
+
|
702
|
+
options['HostConfig'] = host_config = {}
|
703
|
+
host_config['Binds'] = [
|
704
|
+
[File.expand_path(".ssh/id_rsa", ENV['HOME']), "/root/.ssh/id_rsa", 'ro'].join(':'),
|
705
|
+
[dir, "/src/#{project_name}"].join(':'),
|
706
|
+
] + Array(cmd_options[:bind])
|
707
|
+
|
708
|
+
if global_options[:'local-bundle']
|
709
|
+
host_config['Binds']
|
710
|
+
.push([vendor_dir, "/src/#{project_name}/vendor"].join(':'))
|
711
|
+
.push([dot_bundle_dir, "/src/#{project_name}/.bundle"].join(':'))
|
712
|
+
end
|
713
|
+
|
714
|
+
host_config['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
715
|
+
host_config['VolumesFrom'] = cmd_options[:'volumes-from'] unless cmd_options[:'volumes-from'].empty?
|
716
|
+
|
717
|
+
add_network_config(options, cmd_options)
|
718
|
+
|
719
|
+
unless cmd_options[:port].empty?
|
720
|
+
port_bindings = Hash.new({})
|
721
|
+
cmd_options[:port].each do |mapping|
|
722
|
+
hport, cport = mapping.split(':')
|
723
|
+
port_bindings["#{cport}/tcp"] = [{'HostPort' => hport}]
|
724
|
+
end
|
725
|
+
host_config['PortBindings'] = port_bindings
|
726
|
+
end
|
727
|
+
|
728
|
+
if cmd_options[:kill]
|
729
|
+
previous = Docker::Container.get(options['name']) rescue nil
|
730
|
+
previous.delete(:force => true) if previous
|
731
|
+
end
|
732
|
+
|
733
|
+
container = Docker::Container.create(options.tap { |o| DebugMixin.debug_write "creating container with options #{o.inspect}" })
|
734
|
+
$stdout.puts container.id
|
735
|
+
container.start!
|
736
|
+
|
737
|
+
wait_for_conjur appliance_image, container
|
738
|
+
|
739
|
+
if cmd_options[:'dev-install']
|
740
|
+
container_command(container, "/opt/conjur/evoke/bin/dev-install", project_name)
|
741
|
+
container_command(container, 'sv', 'restart', "conjur/#{project_name}")
|
742
|
+
end
|
743
|
+
|
744
|
+
if cmd_options[:command]
|
745
|
+
container_command(container, '/bin/bash', '-c', cmd_options[:command])
|
746
|
+
end
|
747
|
+
end
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
desc "Publish a debian package to apt repository"
|
752
|
+
long_desc <<DESC
|
753
|
+
Publishes a deb created with `debify package` to our private apt repository.
|
754
|
+
|
755
|
+
"distribution" should match the major/minor version of the Conjur appliance you want to install to.
|
756
|
+
|
757
|
+
The package name is a required option. The package version can be specified as a CLI option, or it will
|
758
|
+
be auto-detected from Git.
|
759
|
+
|
760
|
+
--component should be 'stable' if run after package tests pass or 'testing' if the package is not yet ready for release.
|
761
|
+
If you don't specify the component, it will be set to 'testing' unless the current git branch is 'master' or 'origin/master'.
|
762
|
+
The git branch is first detected from the env var GIT_BRANCH or BRANCH_NAME, and then by checking `git rev-parse --abbrev-ref HEAD`
|
763
|
+
(which won't give you the answer you want when detached).
|
764
|
+
|
765
|
+
DESC
|
766
|
+
arg_name "distribution project-name"
|
767
|
+
command "publish" do |c|
|
768
|
+
c.desc "Set the current working directory"
|
769
|
+
c.flag [:d, :dir]
|
770
|
+
|
771
|
+
c.desc "Specify the deb package version; by default, it's computed automatically"
|
772
|
+
c.flag [:v, :version]
|
773
|
+
|
774
|
+
c.desc "Component to publish to, either 'stable' or the name of the git branch"
|
775
|
+
c.flag [:c, :component]
|
776
|
+
|
777
|
+
c.desc "Artifactory URL to publish to"
|
778
|
+
c.default_value "https://conjurinc.jfrog.io/conjurinc"
|
779
|
+
c.flag [:u, :url]
|
780
|
+
|
781
|
+
c.desc "Artifactory Debian repo to publish package to"
|
782
|
+
c.default_value "debian-private"
|
783
|
+
c.flag [:r, :repo]
|
784
|
+
|
785
|
+
c.desc "Artifactory RPM repo to publish package to"
|
786
|
+
c.default_value "redhat-private"
|
787
|
+
c.flag ['rpm-repo']
|
788
|
+
|
789
|
+
c.action do |global_options, cmd_options, args|
|
790
|
+
require 'conjur/debify/action/publish'
|
791
|
+
raise "distribution is required" unless distribution = args.shift
|
792
|
+
raise "project-name is required" unless project_name = args.shift
|
793
|
+
raise "Received extra command-line arguments" if args.shift
|
794
|
+
|
795
|
+
Conjur::Debify::Action::Publish.new(distribution, project_name, cmd_options).run
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
desc "Auto-detect and print the repository version"
|
800
|
+
command "detect-version" do |c|
|
801
|
+
c.desc "Set the current working directory"
|
802
|
+
c.flag [:d, :dir]
|
803
|
+
c.action do |global_options, cmd_options, args|
|
804
|
+
raise "Received extra command-line arguments" if args.shift
|
805
|
+
|
806
|
+
dir = cmd_options[:dir] || '.'
|
807
|
+
dir = File.expand_path(dir)
|
808
|
+
|
809
|
+
raise "Directory #{dir} does not exist or is not a directory" unless File.directory?(dir)
|
810
|
+
|
811
|
+
Dir.chdir dir do
|
812
|
+
puts detect_version
|
813
|
+
end
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
desc 'Show the given configuration'
|
818
|
+
arg_name 'configuration'
|
819
|
+
command 'config' do |c|
|
820
|
+
c.action do |_, _, args|
|
821
|
+
raise 'no configuration provided' unless config = args.shift
|
822
|
+
raise "Received extra command-line arguments" if args.shift
|
823
|
+
|
824
|
+
File.open(File.join('distrib', config)).each do |line|
|
825
|
+
puts line.gsub(/@@DEBIFY_VERSION@@/, Conjur::Debify::VERSION)
|
826
|
+
end
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
|
831
|
+
pre do |global, command, options, args|
|
832
|
+
# Pre logic here
|
833
|
+
# Return true to proceed; false to abort and not call the
|
834
|
+
# chosen command
|
835
|
+
# Use skips_pre before a command to skip this block
|
836
|
+
# on that command only
|
837
|
+
true
|
838
|
+
end
|
839
|
+
|
840
|
+
post do |global, command, options, args|
|
841
|
+
# Post logic here
|
842
|
+
# Use skips_post before a command to skip this
|
843
|
+
# block on that command only
|
844
|
+
end
|
845
|
+
|
846
|
+
on_error do |exception|
|
847
|
+
# Error logic here
|
848
|
+
# return false to skip default error handling
|
849
|
+
true
|
850
|
+
end
|
851
|
+
|