fidoci 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|