fidoci 0.1.1 → 0.1.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/bin/d +5 -5
- data/lib/fidoci/env.rb +256 -23
- data/lib/fidoci/main.rb +24 -15
- data/lib/fidoci.rb +2 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50e39b8284a7f267079d82b4386bb5a7d0ba8727
|
4
|
+
data.tar.gz: 244499f405776ab68b08560320d94a004047f0fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93563c1ab7a9f6e6e725aa1c3c9a4220a0fce1c5f7fc26a649667771bef1f1b09dbdaeb19d36c11db8c1ecdf88dcc0d4be0502817e38e8d21f5276fca7cade9b
|
7
|
+
data.tar.gz: 3674e2bf7980ad3cf17a362e505323fd2bd53ef51b0db85d9fd2aea65bb4555bc6a8de2c753fbe0b61bf275f82bd1062c818de9bebbe460a3f69f9f1ccf61f62
|
data/bin/d
CHANGED
@@ -10,8 +10,8 @@ if ARGV.size == 0
|
|
10
10
|
puts
|
11
11
|
puts "Options:"
|
12
12
|
puts
|
13
|
-
puts " --build [
|
14
|
-
puts " --clean
|
13
|
+
puts " --build success-tag [build-id] Builds 'build_id' image, run tests inside and if successful, tag image by given 'tag'"
|
14
|
+
puts " --clean Removes all containers and images from docker related to this repo"
|
15
15
|
puts
|
16
16
|
puts "Command:"
|
17
17
|
puts " Running with command will build 'exec' image, start docker-compose with that image and run given command in container"
|
@@ -31,10 +31,10 @@ when '--clean'
|
|
31
31
|
# clean service and intermediate docker images
|
32
32
|
environment.clean
|
33
33
|
when '--build'
|
34
|
-
# d --build
|
34
|
+
# d --build success-tag build-id
|
35
35
|
# build image, test it and if successful, tag as latest-staging
|
36
36
|
# if --clean is present, will clean all intermediate docker images after
|
37
|
-
if environment.build(ARGV.
|
37
|
+
if environment.build(ARGV.drop(1)[0], ARGV.drop(1)[1])
|
38
38
|
exit 0
|
39
39
|
else
|
40
40
|
exit 1
|
@@ -43,5 +43,5 @@ else
|
|
43
43
|
# d cmd args args
|
44
44
|
# run cmd in exec environment
|
45
45
|
# build container image and run all services if not yet
|
46
|
-
environment.cmd(*ARGV)
|
46
|
+
exit environment.cmd(*ARGV)
|
47
47
|
end
|
data/lib/fidoci/env.rb
CHANGED
@@ -10,60 +10,293 @@ module Fidoci
|
|
10
10
|
@env_config = env_config
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
if
|
15
|
-
|
16
|
-
|
13
|
+
def debug(msg)
|
14
|
+
puts msg if ENV['DEBUG']
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(msg)
|
18
|
+
puts msg
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_image
|
22
|
+
image = Docker::Image.get(image_name) rescue nil
|
23
|
+
unless image
|
24
|
+
image = do_build_image
|
17
25
|
end
|
18
26
|
|
19
|
-
|
20
|
-
|
27
|
+
image
|
28
|
+
end
|
29
|
+
|
30
|
+
def do_build_image
|
31
|
+
info "Building image #{image_name}..."
|
32
|
+
params = {}
|
21
33
|
|
22
|
-
params
|
23
|
-
params
|
34
|
+
params['forcerm'] = true
|
35
|
+
params['t'] = image_name
|
24
36
|
if env_config['dockerfile']
|
25
|
-
params
|
37
|
+
params['dockerfile'] = env_config['dockerfile']
|
38
|
+
end
|
39
|
+
|
40
|
+
last_intermediate_image = nil
|
41
|
+
image = Docker::Image.build_from_dir('.', params) do |chunk|
|
42
|
+
json = JSON.parse(chunk)
|
43
|
+
if (json['stream'] =~ /\ --->\ ([a-f0-9]{12})/) == 0
|
44
|
+
last_intermediate_image = $1
|
45
|
+
end
|
46
|
+
$stdout << json['stream']
|
26
47
|
end
|
48
|
+
last_intermediate_image = nil
|
27
49
|
|
28
|
-
|
50
|
+
image
|
51
|
+
ensure
|
52
|
+
begin
|
53
|
+
if last_intermediate_image
|
54
|
+
img = Docker::Image.get(last_intermediate_image)
|
55
|
+
img.json
|
56
|
+
puts "Removing intermediate image #{last_intermediate_image}"
|
57
|
+
img.remove('force' => true)
|
58
|
+
end
|
59
|
+
rescue
|
60
|
+
nil
|
61
|
+
end
|
29
62
|
end
|
30
63
|
|
31
64
|
def image_name
|
32
65
|
"%s:%s" % [@image, @name]
|
33
66
|
end
|
34
67
|
|
68
|
+
def tag_image(tag)
|
69
|
+
image = Docker::Image.get(image_name)
|
70
|
+
image.tag(@image => tag)
|
71
|
+
end
|
72
|
+
|
73
|
+
def container_name
|
74
|
+
@image.gsub(/[^a-zA-Z0-9_]/, '_') + "_" + @name.gsub(/[^a-zA-Z0-9_]/, '_')
|
75
|
+
end
|
76
|
+
|
35
77
|
def clean_image!
|
36
|
-
|
78
|
+
begin
|
79
|
+
debug "Cleaning image #{image_name}"
|
80
|
+
image = Docker::Image.get(image_name)
|
81
|
+
image.remove(force: true)
|
82
|
+
rescue
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def clean_container(cname)
|
88
|
+
begin
|
89
|
+
debug "Cleaning container #{cname}..."
|
90
|
+
container = Docker::Container.get(cname)
|
91
|
+
container.remove(force: true)
|
92
|
+
rescue
|
93
|
+
debug "Cleaning failed"
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def links
|
99
|
+
env_config['links'] || []
|
37
100
|
end
|
38
101
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
102
|
+
def clean_containers!
|
103
|
+
containers = [container_name]
|
104
|
+
containers += links.map{|key, config| link_container_name(key) }
|
105
|
+
|
106
|
+
containers.each{|cname|
|
107
|
+
clean_container(cname)
|
44
108
|
}
|
109
|
+
true
|
110
|
+
end
|
45
111
|
|
46
|
-
|
112
|
+
# clean images and containers associated with this Env
|
113
|
+
def clean!
|
114
|
+
clean_containers! rescue nil
|
115
|
+
clean_image! rescue nil
|
116
|
+
end
|
117
|
+
|
118
|
+
# stop services and clean main container
|
119
|
+
def stop!
|
120
|
+
links.map{|key, config|
|
121
|
+
cname = link_container_name(key)
|
122
|
+
begin
|
123
|
+
debug "Stopping container #{cname}..."
|
124
|
+
container = Docker::Container.get(cname)
|
125
|
+
container.stop!
|
126
|
+
rescue
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
}
|
47
130
|
end
|
48
131
|
|
132
|
+
# run command in docker, building the image and starting services first
|
133
|
+
# if needed
|
49
134
|
def cmd(*args)
|
50
|
-
if build_image
|
51
|
-
|
52
|
-
|
135
|
+
if build_image && link_containers
|
136
|
+
debug "Running `#{args.join(' ')}`..."
|
137
|
+
docker_run_or_exec(*args)
|
53
138
|
else
|
54
|
-
|
139
|
+
debug "Build failed"
|
55
140
|
return false
|
56
141
|
end
|
57
142
|
end
|
58
143
|
|
144
|
+
def link_container_name(key)
|
145
|
+
"#{container_name}_#{key}"
|
146
|
+
end
|
147
|
+
|
148
|
+
# starts link containers and return dict name:container_name
|
149
|
+
def link_containers
|
150
|
+
links = env_config['links'] || []
|
151
|
+
|
152
|
+
links.map {|key, link_config|
|
153
|
+
[key, start_link(link_container_name(key), link_config)]
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def start_link(link_container_name, link_config)
|
158
|
+
container = Docker::Container.get(link_container_name) rescue nil
|
159
|
+
|
160
|
+
unless container
|
161
|
+
params = {}
|
162
|
+
params['name'] = link_container_name
|
163
|
+
params['Image'] = link_config['image']
|
164
|
+
|
165
|
+
config_params(params, link_config)
|
166
|
+
|
167
|
+
debug "Creating container #{link_container_name}..."
|
168
|
+
container = Docker::Container.create(params)
|
169
|
+
end
|
170
|
+
|
171
|
+
unless container.json['State']['Running']
|
172
|
+
debug "Starting container #{link_container_name}..."
|
173
|
+
container.start!
|
174
|
+
end
|
175
|
+
|
176
|
+
debug "Using container #{link_container_name}..."
|
177
|
+
|
178
|
+
link_container_name
|
179
|
+
end
|
180
|
+
|
181
|
+
def docker_run_or_exec(*args)
|
182
|
+
container = Docker::Container.get(container_name) rescue nil
|
183
|
+
|
184
|
+
if container
|
185
|
+
docker_exec(container.id, *args)
|
186
|
+
else
|
187
|
+
docker_run(*args)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def docker_exec(id, *args)
|
192
|
+
params = {}
|
193
|
+
params["Container"] = id
|
194
|
+
params["AttachStdin"] = true
|
195
|
+
params["AttachStdout"] = true
|
196
|
+
params["AttachStderr"] = true
|
197
|
+
params["Tty"] = true
|
198
|
+
params["Cmd"] = args
|
199
|
+
|
200
|
+
docker_exec = Docker::Exec.create(params)
|
201
|
+
result = docker_exec.start!(tty: true, stdin: $stdin) do |msg|
|
202
|
+
$stdout << msg
|
203
|
+
end
|
204
|
+
|
205
|
+
debug "Exited with status #{result[2]}"
|
206
|
+
|
207
|
+
result[2]
|
208
|
+
end
|
209
|
+
|
210
|
+
# calls docker run command with all needed parameters
|
211
|
+
# attaches stdin and stdout
|
212
|
+
def docker_run(*args)
|
213
|
+
params = {}
|
214
|
+
params['AttachStdin'] = true
|
215
|
+
params['OpenStdin'] = true
|
216
|
+
params['Tty'] = true
|
217
|
+
params['name'] = container_name
|
218
|
+
|
219
|
+
# links
|
220
|
+
link_containers.each{|name, container_name|
|
221
|
+
params['HostConfig'] ||= {}
|
222
|
+
params['HostConfig']['Links'] ||= []
|
223
|
+
params['HostConfig']['Links'] << "#{container_name}:#{name}"
|
224
|
+
}
|
225
|
+
|
226
|
+
config_params(params, env_config)
|
227
|
+
params['Image'] = image_name
|
228
|
+
params['Cmd'] = args
|
229
|
+
|
230
|
+
#puts params
|
231
|
+
|
232
|
+
container = Docker::Container.create(params)
|
233
|
+
|
234
|
+
container.start!.attach(stdin: $stdin, tty: true) do |msg|
|
235
|
+
$stdout << msg
|
236
|
+
end
|
237
|
+
|
238
|
+
status = container.json['State']['ExitCode']
|
239
|
+
debug "Exited with status #{status}"
|
240
|
+
|
241
|
+
status
|
242
|
+
ensure
|
243
|
+
clean_container(container_name)
|
244
|
+
end
|
245
|
+
|
246
|
+
def config_params(params, config)
|
247
|
+
params['HostConfig'] ||= {}
|
248
|
+
|
249
|
+
# env
|
250
|
+
if config['environment']
|
251
|
+
config['environment'].each {|key,val|
|
252
|
+
params['Env'] ||= []
|
253
|
+
params['Env'] << "#{key}=#{val}"
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
# volumes
|
258
|
+
if config['volumes']
|
259
|
+
config['volumes'].each {|v|
|
260
|
+
params['HostConfig']['Binds'] ||= []
|
261
|
+
|
262
|
+
host_path, container_path = v.split(':')
|
263
|
+
host_path = File.expand_path(host_path)
|
264
|
+
|
265
|
+
params['HostConfig']['Binds'] << "#{host_path}:#{container_path}"
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
# ports
|
270
|
+
if config['ports']
|
271
|
+
config['ports'].each {|p|
|
272
|
+
parts = p.split(':')
|
273
|
+
container_port = parts.last
|
274
|
+
host_port = parts[-2]
|
275
|
+
host_ip = parts[-3] || ""
|
276
|
+
|
277
|
+
params['HostConfig']['PortBindings'] ||= {}
|
278
|
+
params['HostConfig']['PortBindings']["#{container_port}/tcp"] = [
|
279
|
+
{
|
280
|
+
"HostIp" => host_ip,
|
281
|
+
"HostPort" => host_port
|
282
|
+
}
|
283
|
+
]
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
params
|
288
|
+
end
|
289
|
+
|
59
290
|
def commands
|
60
291
|
return false unless env_config['commands']
|
61
292
|
|
62
293
|
success = env_config['commands'].all? { |command|
|
63
|
-
cmd
|
294
|
+
state = cmd(*command.split(/\s+/))
|
295
|
+
info "Exited with state #{state}"
|
296
|
+
|
297
|
+
state == 0
|
64
298
|
}
|
65
299
|
|
66
|
-
puts "Test failed" unless success
|
67
300
|
success
|
68
301
|
end
|
69
302
|
end
|
data/lib/fidoci/main.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'docker'
|
2
4
|
|
3
5
|
module Fidoci
|
4
6
|
# Main entry point for D command
|
@@ -11,14 +13,20 @@ module Fidoci
|
|
11
13
|
# config_file - yml file path with configuration
|
12
14
|
def initialize(config_file = 'd.yml')
|
13
15
|
@config = YAML.load_file(config_file)
|
14
|
-
|
16
|
+
|
17
|
+
Docker.options = {
|
18
|
+
read_timeout: 3600
|
19
|
+
}
|
15
20
|
end
|
16
21
|
|
17
22
|
# Run command in default "exec" environment
|
18
23
|
# ie in container build and started with exec configuration
|
19
24
|
# args - command and arguments to pass to docker run command
|
20
25
|
def cmd(*args)
|
21
|
-
env(:
|
26
|
+
exec_env = env(:dev, 'dev')
|
27
|
+
exec_env.cmd(*args)
|
28
|
+
ensure
|
29
|
+
exec_env.stop!
|
22
30
|
end
|
23
31
|
|
24
32
|
# Configured docker repository name
|
@@ -29,38 +37,39 @@ module Fidoci
|
|
29
37
|
|
30
38
|
# Create environment instance with given name
|
31
39
|
# name - key that will be used to configure this env
|
32
|
-
|
33
|
-
|
40
|
+
# id - unique identifier of env that will be used to tag containers and images
|
41
|
+
def env(name, id)
|
42
|
+
Env.new(repository_name, id.to_s, config[name.to_s])
|
34
43
|
end
|
35
44
|
|
36
45
|
# Clean system
|
37
46
|
# removes all service and running containers and their images
|
38
47
|
# and removes all images build by d
|
39
48
|
def clean
|
40
|
-
system 'docker-compose kill'
|
41
|
-
system 'docker-compose rm -f'
|
42
|
-
|
43
49
|
(config.keys - ['image']).each { |name|
|
44
|
-
env = env(name)
|
45
|
-
env.
|
50
|
+
env = env(name, name)
|
51
|
+
env.clean!
|
46
52
|
}
|
47
53
|
end
|
48
54
|
|
49
55
|
# Build image and run test in it
|
50
56
|
# tag - tag name to tag image after successful build and test
|
51
|
-
#
|
52
|
-
def build(tag,
|
53
|
-
|
54
|
-
|
57
|
+
# build_id - unique build_id to be used to identify docker images and containers
|
58
|
+
def build(tag, build_id)
|
59
|
+
build_id = SecureRandom.hex(10) unless build_id
|
60
|
+
|
61
|
+
test_env = env(:build, build_id)
|
62
|
+
test_env.clean!
|
63
|
+
|
55
64
|
success = test_env.commands
|
56
65
|
|
57
66
|
if success
|
58
|
-
|
67
|
+
test_env.tag_image(tag)
|
59
68
|
end
|
60
69
|
|
61
70
|
success
|
62
71
|
ensure
|
63
|
-
clean if
|
72
|
+
test_env.clean! if test_env
|
64
73
|
end
|
65
74
|
end
|
66
75
|
end
|
data/lib/fidoci.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fidoci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lukas Dolezal
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
12
|
-
dependencies:
|
11
|
+
date: 2015-07-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: docker-api
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
description: Simple tool around docker-compose to enable dockerized dev-2-test-2-production
|
14
28
|
workflow
|
15
29
|
email: lukas@dolezalu.cz
|