docker-rails-app 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/docker_rails_app/import_gem_rake_tasks.rb +9 -0
- data/lib/docker_rails_app/version.rb +3 -0
- data/lib/docker_rails_app.rb +5 -0
- data/lib/support/docker_api.rb +81 -0
- data/lib/support/docker_image_creator.rb +244 -0
- data/lib/support/docker_rails_app_creator.rb +60 -0
- data/lib/support/docker_rails_config_reader.rb +26 -0
- data/lib/support/docker_registry.rb +58 -0
- data/lib/support/docker_runner.rb +263 -0
- data/lib/support/image_files/DockerfileRepo +49 -0
- data/lib/support/image_files/DockerfileSelf +46 -0
- data/lib/support/image_files/entrypoint.sh +16 -0
- data/lib/support/image_files/wait_for_file.sh +9 -0
- data/lib/support/image_files/wait_for_port.sh +11 -0
- data/lib/support/initialize_project.rb +131 -0
- data/lib/support/launch_support.rb +62 -0
- data/lib/tasks/deploy.rake +45 -0
- data/lib/tasks/initialize.rake +13 -0
- data/lib/tasks/run.rake +38 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 89604320070bdd27a69e302b9ab8293462a8bbd7
|
4
|
+
data.tar.gz: 4e633b7d7f29fc3f07233be921023285f967178b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0df7c6014209c36622f3845abdd89a8cff2c1f7f2b3f98773626795dafd9dc6713f0183eb336c5248aaee242591d7aaba1735bf4e1305841742f4542995dea1
|
7
|
+
data.tar.gz: 2a0ea6f57497d5702589e3fa8b7bde7a8d8094bc58c0ecfa2c58a6390f2e994c6586a5fb084039634c5a0090ce641275768163f361066a87eb464b459affa77e
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "docker_rails_app"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# DOCKER_URL=tcp://qa2:4243 ruby lib/docker-api/docker_api_tests.rb
|
2
|
+
# DOCKER_URL=tcp://192.168.99.100:2376 ruby lib/docker-api/docker_api_tests.rb
|
3
|
+
require 'docker'
|
4
|
+
|
5
|
+
raise "Remove this monkey patch when API library provides this stats method" if (Docker::Container.respond_to? "stats")
|
6
|
+
class Docker::Container
|
7
|
+
# Non streaming stats
|
8
|
+
def stats(options = {})
|
9
|
+
path = path_for(:stats)
|
10
|
+
puts "Getting stats from: #{path}"
|
11
|
+
options[:stream] = false
|
12
|
+
container_stats = nil
|
13
|
+
begin
|
14
|
+
status = Timeout::timeout(5) {
|
15
|
+
container_stats = connection.get(path, options)
|
16
|
+
}
|
17
|
+
rescue Exception => e
|
18
|
+
return {error: e.message}
|
19
|
+
end
|
20
|
+
JSON.parse(container_stats)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class DockerApi
|
25
|
+
attr_accessor :connection
|
26
|
+
|
27
|
+
# Build a connection to docker on a remote host. Need a connection to be able to make multiple docker calls within the app to different hosts.
|
28
|
+
# host: {address: "olex-qa2.openlogic.com", docker_port: 4245 }
|
29
|
+
def self.connection(host)
|
30
|
+
cert_path = ENV['DOCKER_CERT_PATH']
|
31
|
+
scheme = "http"
|
32
|
+
scheme = "https" if (ENV['DOCKER_TLS_VERIFY'] == "1")
|
33
|
+
|
34
|
+
docker_connection_opts = {:client_cert=>"#{cert_path}/cert.pem", :client_key=>"#{cert_path}/key.pem",
|
35
|
+
:ssl_ca_file=>"#{cert_path}/ca.pem", :scheme => scheme}
|
36
|
+
|
37
|
+
# docker_connection_opts = {:client_cert=>"/Users/mikemoore/.docker/machine/machines/dev/cert.pem", :client_key=>"/Users/mikemoore/.docker/machine/machines/dev/key.pem",
|
38
|
+
# :ssl_ca_file=>"/Users/mikemoore/.docker/machine/machines/dev/ca.pem", :scheme=>"https"}
|
39
|
+
docker_connection_opts[:scheme] = "http" if (host[:ssl] == false)
|
40
|
+
docker_connection = Docker::Connection.new("tcp://#{host[:address]}:#{host[:docker_port]}", docker_connection_opts)
|
41
|
+
return docker_connection
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(url, secure = true)
|
45
|
+
# Docker.logger = Logger.new(STDOUT)
|
46
|
+
# Docker.logger.level = Logger::DEBUG
|
47
|
+
address = url.split("//").last.split(":").first
|
48
|
+
port = url.split(":").last
|
49
|
+
host = {address: address, docker_port: port, ssl: secure}
|
50
|
+
@connection = DockerApi.connection(host)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_container_with_name(wanted_name)
|
54
|
+
options = {
|
55
|
+
all: true
|
56
|
+
}
|
57
|
+
all_containers = Docker::Container.all(options, @connection)
|
58
|
+
all_containers.each do |container|
|
59
|
+
json = container.json
|
60
|
+
name = json['Name']
|
61
|
+
return container if (name == "/#{wanted_name}")
|
62
|
+
end
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
#
|
69
|
+
# puts ""
|
70
|
+
# puts "Here are Containers on #{docker_url}"
|
71
|
+
# all_containers = Docker::Container.all
|
72
|
+
# all_containers.each do |container|
|
73
|
+
# puts "#{container.json['Name']}"
|
74
|
+
# puts " Running: #{container.json['State']['Running']}"
|
75
|
+
# puts " Running: #{container.json['State']['ExitCode']}"
|
76
|
+
#
|
77
|
+
# env_vars = container.json['Config']['Env']
|
78
|
+
# env_vars.each do |env|
|
79
|
+
# puts " Env: #{env}"
|
80
|
+
# end
|
81
|
+
# end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require_relative "./docker_runner"
|
2
|
+
require_relative "./docker_registry"
|
3
|
+
|
4
|
+
class DockerImageCreator
|
5
|
+
attr_accessor :use_date_as_version
|
6
|
+
|
7
|
+
REMOVE_INTERMEDIATE_FLAG = "--rm=true"
|
8
|
+
DAEMON_OPTIONS = " -d -t -P"
|
9
|
+
INTERACT_OPTIONS = " -a stdin -a stdout -i -t -P"
|
10
|
+
DOCKER_DIR = Dir.pwd + "/lib"
|
11
|
+
|
12
|
+
attr_accessor :version_forced, :image_name_forced, :docker_host, :docker_host_secure
|
13
|
+
attr_reader :definition
|
14
|
+
|
15
|
+
def initialize(definition)
|
16
|
+
raise "Definition is nil" if definition == nil
|
17
|
+
@definition = definition
|
18
|
+
@home = FileUtils.pwd
|
19
|
+
@version = nil
|
20
|
+
@docker_host = nil
|
21
|
+
@docker_host_secure = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def use_date_as_version?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def image_name
|
29
|
+
return (@image_name_forced) if (@image_name_forced)
|
30
|
+
build_def = @definition[:build]
|
31
|
+
name = @definition[:run][:image_name] if (@definition[:run])
|
32
|
+
name = build_def[:name] if (build_def)
|
33
|
+
return name
|
34
|
+
end
|
35
|
+
|
36
|
+
def docker_api
|
37
|
+
@docker_host = @docker_host || ENV['DOCKER_HOST']
|
38
|
+
@docker_host_secure = @docker_host_secure || (ENV['DOCKER_TLS_VERIFY'].to_s == '1')
|
39
|
+
raise "Need to define DOCKER_HOST env variable" if (!@docker_host)
|
40
|
+
raise "Need to define DOCKER_TLS_VERIFY env variable" if (@docker_host_secure == nil)
|
41
|
+
docker_api = DockerApi.new(@docker_host, @docker_host_secure)
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_it()
|
45
|
+
docker_connection = docker_api.connection
|
46
|
+
build_def = @definition[:build]
|
47
|
+
if (build_def == nil)
|
48
|
+
puts "Warning: build not defined for #{definition[:code]} - ignoring request"
|
49
|
+
else
|
50
|
+
begin
|
51
|
+
before_build(@definition)
|
52
|
+
remove_intermediate = REMOVE_INTERMEDIATE_FLAG
|
53
|
+
|
54
|
+
volume_path = build_def[:volume_path]
|
55
|
+
build_command = nil
|
56
|
+
|
57
|
+
image_and_version = image_name()
|
58
|
+
image_and_version += ":#{version()}" if (version() != nil)
|
59
|
+
|
60
|
+
opts = build_def[:api_options] || {}
|
61
|
+
opts[:t] = "#{image_and_version}"
|
62
|
+
opts[:rm] = true
|
63
|
+
|
64
|
+
Excon.defaults[:write_timeout] = 1000
|
65
|
+
Excon.defaults[:read_timeout] = 1000
|
66
|
+
puts "Building image. Docker dir: #{docker_dir} ..."
|
67
|
+
|
68
|
+
image = Docker::Image.build_from_dir(docker_dir, opts, docker_connection) do |v|
|
69
|
+
begin
|
70
|
+
if (log = JSON.parse(v)) && log.has_key?("stream")
|
71
|
+
$stdout.puts log["stream"]
|
72
|
+
end
|
73
|
+
rescue Exception => ex
|
74
|
+
puts "#{v}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
rescue Exception => ex
|
78
|
+
puts"Exception: #{ex.message}"
|
79
|
+
puts ex.backtrace
|
80
|
+
debugger
|
81
|
+
raise "Exception building image. #{ex.message}\nCheck connection to: #{docker_connection}"
|
82
|
+
ensure
|
83
|
+
after_build(@definition)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def version
|
89
|
+
return (@version_forced) if (@version_forced)
|
90
|
+
build_def = @definition[:build]
|
91
|
+
raise "No build definition for: #{@definition[:code]}" if (!build_def)
|
92
|
+
version = 'latest'
|
93
|
+
version = build_def[:version] if (build_def[:version] != nil)
|
94
|
+
return version
|
95
|
+
end
|
96
|
+
|
97
|
+
def tag_it(registry)
|
98
|
+
raise "DOCKER_REGISTRY environment variable not defined for push operation" if registry == nil
|
99
|
+
do_tag(registry)
|
100
|
+
end
|
101
|
+
|
102
|
+
#def do_tag(image_name, version, registry)
|
103
|
+
def do_tag(registry)
|
104
|
+
docker_connection = docker_api.connection
|
105
|
+
image = Docker::Image.get("#{image_name}:#{version}", {}, docker_connection)
|
106
|
+
puts "Tagging image: #{image_name}"
|
107
|
+
image.tag('repo' => "#{registry}/#{image_name()}", 'image' => 'unicorn', 'tag' => version(), force: true)
|
108
|
+
end
|
109
|
+
|
110
|
+
def push_it(registry)
|
111
|
+
raise "DOCKER_REGISTRY environment variable not defined for push operation" if registry == nil
|
112
|
+
build_def = @definition[:build]
|
113
|
+
do_push(registry)
|
114
|
+
end
|
115
|
+
|
116
|
+
def api_image(registry, docker_connection)
|
117
|
+
full_name = "#{registry}/#{image_name}"
|
118
|
+
full_name += ":#{version()}" if (version() != nil)
|
119
|
+
image = Docker::Image.get(full_name, {}, docker_connection)
|
120
|
+
puts "Pushing Image: #{full_name} ..."
|
121
|
+
return image
|
122
|
+
end
|
123
|
+
|
124
|
+
def do_push(registry)
|
125
|
+
if (registry)
|
126
|
+
Excon.defaults[:write_timeout] = 1000
|
127
|
+
Excon.defaults[:read_timeout] = 1000
|
128
|
+
|
129
|
+
docker_connection = docker_api.connection
|
130
|
+
|
131
|
+
image = api_image(registry, docker_connection)
|
132
|
+
|
133
|
+
credentials = nil
|
134
|
+
result = image.push(credentials, {tag: version, repo: registry})
|
135
|
+
puts "Done pushing Image: #{registry}/#{image_name}"
|
136
|
+
the_registry = DockerRegistry.new(registry)
|
137
|
+
|
138
|
+
if (the_registry.has_image_with_version?("#{image_name}", version) == false)
|
139
|
+
raise "After push, image doesn't appear in registry. Image: #{image_name}:#{version}. Push result: #{result}"
|
140
|
+
end
|
141
|
+
else
|
142
|
+
puts "No registry specified for pushing"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def run_it(host, host_ssl, interact, run_instance_count, registry = nil)
|
147
|
+
@docker_host = host
|
148
|
+
@docker_host_secure = host_ssl
|
149
|
+
puts "Warning: registry not defined, assuming run is local" if registry == nil
|
150
|
+
run_def = @definition[:run]
|
151
|
+
build_def = @definition[:build]
|
152
|
+
if (run_def == nil)
|
153
|
+
puts "No run definition for: #{@definition[:code]}"
|
154
|
+
else
|
155
|
+
(1..run_instance_count).each do |instance_index|
|
156
|
+
use_extension = (run_instance_count > 1)
|
157
|
+
run_with_extension(:next, registry, interact, use_extension)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def run_with_extension(instance_index, registry, interact, use_extension)
|
163
|
+
@docker_api = DockerApi.new(@docker_host)
|
164
|
+
docker_runner = DockerRunner.new(self, nil)
|
165
|
+
docker_runner.registry = registry
|
166
|
+
docker_runner.instance_index = instance_index
|
167
|
+
docker_runner.use_extension = use_extension
|
168
|
+
docker_runner.interact = interact
|
169
|
+
docker_runner.image_name = image_name
|
170
|
+
docker_runner.code = @definition[:code]
|
171
|
+
docker_runner.remote_address = @docker_host
|
172
|
+
docker_runner.secure_docker_api = @docker_host_secure
|
173
|
+
docker_runner.run_with_extension
|
174
|
+
end
|
175
|
+
|
176
|
+
def add_it(host, host_ssl, run_instance_count, registry)
|
177
|
+
@docker_host = host
|
178
|
+
@docker_host_secure = host_ssl
|
179
|
+
number_added = 0
|
180
|
+
while (number_added < run_instance_count)
|
181
|
+
run_def = @definition[:run]
|
182
|
+
run_name = run_def[:name]
|
183
|
+
extension = 1
|
184
|
+
run_with_extension(:next, registry, false, true)
|
185
|
+
number_added += 1
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def kill_all(host, host_ssl)
|
190
|
+
docker_runner = DockerRunner.new(self, nil)
|
191
|
+
docker_runner.registry = nil
|
192
|
+
docker_runner.use_extension = true
|
193
|
+
docker_runner.interact = false
|
194
|
+
docker_runner.remote_address = host
|
195
|
+
docker_runner.secure_docker_api = host_ssl
|
196
|
+
|
197
|
+
run_def = @definition[:run]
|
198
|
+
run_name = run_def[:name]
|
199
|
+
docker_runner.kill_all(run_name)
|
200
|
+
end
|
201
|
+
|
202
|
+
def subtract(host, host_ssl, count)
|
203
|
+
docker_runner = DockerRunner.new(self, nil)
|
204
|
+
docker_runner.registry = nil
|
205
|
+
docker_runner.use_extension = true
|
206
|
+
docker_runner.interact = false
|
207
|
+
docker_runner.remote_address = host
|
208
|
+
docker_runner.secure_docker_api = host_ssl
|
209
|
+
|
210
|
+
run_def = @definition[:run]
|
211
|
+
run_name = run_def[:name]
|
212
|
+
docker_runner.kill(run_name, count)
|
213
|
+
end
|
214
|
+
|
215
|
+
def openlogic_home()
|
216
|
+
@openlogic_home = @openlogic_home[0..(@openlogic_home.length - 2)] if (@openlogic_home.end_with? "/")
|
217
|
+
return @openlogic_home
|
218
|
+
end
|
219
|
+
|
220
|
+
def before_build(definition)
|
221
|
+
delete_image_support_from_stage()
|
222
|
+
copy_image_support_to_stage()
|
223
|
+
end
|
224
|
+
|
225
|
+
def after_build(definition)
|
226
|
+
delete_image_support_from_stage()
|
227
|
+
end
|
228
|
+
|
229
|
+
def delete_image_support_from_stage()
|
230
|
+
FileUtils.remove_dir("#{docker_dir}image_support", true)
|
231
|
+
end
|
232
|
+
|
233
|
+
def docker_dir
|
234
|
+
return @definition[:build][:docker_directory] + "/"
|
235
|
+
end
|
236
|
+
|
237
|
+
def copy_image_support_to_stage
|
238
|
+
raise "Definition nil" if @definition == nil
|
239
|
+
raise "Build Definition nil" if @definition[:build] == nil
|
240
|
+
image_support_dir = "#{FileUtils.pwd}/lib/support/image_support/"
|
241
|
+
FileUtils.cp_r(image_support_dir, docker_dir())
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative "./docker_image_creator"
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class DockerRailsAppCreator < DockerImageCreator
|
5
|
+
ARCHIVE_TMP_FILE = "/tmp/rails_application.tar.gz"
|
6
|
+
|
7
|
+
def docker_dir
|
8
|
+
dir = definition['build']['docker_dir'] || "#{FileUtils.pwd}/docker"
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_build(definition)
|
12
|
+
build_from_current_dir = true
|
13
|
+
stage_files_dir = docker_dir() + "/files"
|
14
|
+
FileUtils.rm_rf stage_files_dir
|
15
|
+
FileUtils.mkdir_p stage_files_dir
|
16
|
+
|
17
|
+
archive_file = ARCHIVE_TMP_FILE
|
18
|
+
`rm -rf #{archive_file}`
|
19
|
+
`tar -czf #{archive_file} --exclude=tmp/* --exclude .git --exclude "*.log" *`
|
20
|
+
FileUtils.mv(archive_file, stage_files_dir)
|
21
|
+
|
22
|
+
create_database_config_file(definition, stage_files_dir)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_database_config_file(definition, directory)
|
26
|
+
db_host_name = definition['run']['name'] + "-db"
|
27
|
+
File.open("#{directory}/database.yml", "w") do |file|
|
28
|
+
content = "
|
29
|
+
#This file generated by Docker Rails when building docker image
|
30
|
+
production:
|
31
|
+
adapter: mysql2
|
32
|
+
database: application_db
|
33
|
+
pool: 5
|
34
|
+
timeout: 5000
|
35
|
+
host: #{db_host_name}
|
36
|
+
port: 3306
|
37
|
+
user: root
|
38
|
+
password: password
|
39
|
+
persistence: :db
|
40
|
+
"
|
41
|
+
file.write(content)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def prepare_for_git_pull
|
46
|
+
ssh_staging_dir = stage_files_dir + "/ssh/"
|
47
|
+
FileUtils.mkdir_p ssh_staging_dir
|
48
|
+
FileUtils.cp "#{ENV['HOME']}/.ssh/id_rsa", ssh_staging_dir
|
49
|
+
FileUtils.cp "#{ENV['HOME']}/.ssh/id_rsa.pub", ssh_staging_dir
|
50
|
+
end
|
51
|
+
|
52
|
+
def after_build(definiton)
|
53
|
+
if (build_source == :current)
|
54
|
+
stage_files_dir = docker_dir() + "/files"
|
55
|
+
FileUtils.rm_f stage_files_dir
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class DockerRailsConfigReader
|
3
|
+
attr_accessor :definitions
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@definitions = nil
|
7
|
+
config_file = "#{FileUtils.pwd}/docker/docker-rails.json"
|
8
|
+
raise "Could not find your project config at: #{config_file} . Did you run: 'rake docker:install' ?" if (!File.exist?(config_file))
|
9
|
+
File.open(config_file, "r") do |file|
|
10
|
+
str = file.read
|
11
|
+
@definitions = JSON.parse(str)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def rails_app_configuration
|
16
|
+
@definitions.each do |definition|
|
17
|
+
name = definition.keys.first
|
18
|
+
if (name == "rails_app")
|
19
|
+
return definition
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class DockerRegistry
|
4
|
+
|
5
|
+
def initialize(registry_url)
|
6
|
+
@server = registry_url
|
7
|
+
if (!registry_url.include? "://")
|
8
|
+
registry_url = "https://#{registry_url}"
|
9
|
+
end
|
10
|
+
@connection = connection(registry_url)
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_images()
|
14
|
+
response = @connection.get "/v2/_catalog"
|
15
|
+
raise "catalog retrieval return error: #{response.status} #{response.body}" if (response.status != 200)
|
16
|
+
json = JSON.parse(response.body)
|
17
|
+
return json['repositories']
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_tags(image_name)
|
21
|
+
response = @connection.get "/v2/#{image_name}/tags/list"
|
22
|
+
raise "Querying tags returned error code: #{response.status}" if (response.status != 200)
|
23
|
+
json = JSON.parse(response.body)
|
24
|
+
return json['tags']
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_image_with_version?(image, commit_id)
|
28
|
+
tags = self.find_tags(image)
|
29
|
+
if (tags != nil)
|
30
|
+
tags.each do |tag|
|
31
|
+
return true if (tag == commit_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
def images_and_versions
|
38
|
+
hash = {}
|
39
|
+
images = find_images()
|
40
|
+
images.each do |image|
|
41
|
+
tags = find_tags(image)
|
42
|
+
hash[image] = tags
|
43
|
+
end
|
44
|
+
return hash
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def connection(address)
|
50
|
+
if (!@service_connection)
|
51
|
+
@service_connection = Faraday.new address, :ssl => {:verify => false}
|
52
|
+
end
|
53
|
+
return @service_connection
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require_relative "launch_support"
|
2
|
+
require_relative "./docker_api"
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class DockerRunner
|
6
|
+
include LaunchSupport
|
7
|
+
attr_accessor :instance_index, :registry, :use_extension, :interact, :image_name, :code
|
8
|
+
attr_accessor :host, :user, :deployment_id, :version, :secure_docker_api, :remote_address
|
9
|
+
attr_accessor :docker_host, :docker_host_secure, :run_untagged
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@config = config
|
14
|
+
@run_config = config['run']
|
15
|
+
@build_config = config['build']
|
16
|
+
@run_untagged = false
|
17
|
+
|
18
|
+
@image_name = @run_config['image_name']
|
19
|
+
if (!@image_name)
|
20
|
+
if (@build_config)
|
21
|
+
@image_name = @build_config['name']
|
22
|
+
raise "Build config does not have 'name'" if (@image_name == nil)
|
23
|
+
else
|
24
|
+
raise "Can't find an image name. Either specify 'name' in build config or 'image_name' in run config."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@registry = @config['registry']
|
29
|
+
|
30
|
+
@instance_index = 0
|
31
|
+
@use_extension = false
|
32
|
+
@interact = false
|
33
|
+
@remote_address = nil
|
34
|
+
@api_container = nil
|
35
|
+
@version = nil
|
36
|
+
@secure_docker_api = true
|
37
|
+
@build_args = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_machine_apis
|
41
|
+
@run_on_apis = []
|
42
|
+
@all_docker_apis_in_cluster = []
|
43
|
+
if (@deploy_config != nil) && (@deploy_config[:hosts] != nil)
|
44
|
+
@deploy_config[:hosts].each do |host_key|
|
45
|
+
host = Hosts::HOSTS[host_key]
|
46
|
+
address = "tcp://#{host[:address]}:#{host[:docker_port]}"
|
47
|
+
docker_api = DockerApi.new(address, host[:ssl])
|
48
|
+
@all_docker_apis_in_cluster << docker_api
|
49
|
+
@run_on_apis << docker_api if (address == @remote_address) || ( @deploy_config[:targets][run_name] == :all)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
# Config not given, we need to create api from remote address and ssl setting. This is the only api for this class.
|
53
|
+
@docker_host = @docker_host || ENV['DOCKER_HOST']
|
54
|
+
@docker_host_secure = @docker_host_secure || (ENV['DOCKER_TLS_VERIFY'].to_s == '1')
|
55
|
+
docker_api = DockerApi.new(@docker_host, @docker_host_secure)
|
56
|
+
|
57
|
+
@all_docker_apis_in_cluster << docker_api
|
58
|
+
@run_on_apis << docker_api
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def run_name
|
63
|
+
return @run_config['name']
|
64
|
+
end
|
65
|
+
|
66
|
+
def image_version
|
67
|
+
return @version if (@version != nil)
|
68
|
+
the_version = 'latest'
|
69
|
+
the_version = @build_config['version'] if (@build_config) && (@build_config['version'] != nil)
|
70
|
+
return the_version
|
71
|
+
end
|
72
|
+
|
73
|
+
def kill_all(name_prefix)
|
74
|
+
find_machine_apis
|
75
|
+
@all_docker_apis_in_cluster.each do |api|
|
76
|
+
#options = {filters: {status: [:running, :exited, :paused, :restarting]}}
|
77
|
+
options = {}
|
78
|
+
containers = Docker::Container.all(options.to_json, api.connection)
|
79
|
+
containers.each do |container|
|
80
|
+
if (container.json["Name"].start_with?("/#{name_prefix}"))
|
81
|
+
pp "Killing and removing: #{container.json["Name"]}"
|
82
|
+
container.kill() if (container.json["State"]["Running"])
|
83
|
+
container.remove() if (container != nil)
|
84
|
+
else
|
85
|
+
pp " * Container didn't match: #{container.json["Name"]}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def run
|
93
|
+
find_machine_apis
|
94
|
+
raise "No run on apis" if (@run_on_apis.count == 0)
|
95
|
+
|
96
|
+
code = @code
|
97
|
+
instance_index = 1
|
98
|
+
if (@instance_index == :next)
|
99
|
+
options = {filters: {status: [:running]}}
|
100
|
+
@active_container_names = []
|
101
|
+
containers = Docker::Container.all(options.to_json, @run_on_apis.first.connection)
|
102
|
+
containers.each do |container|
|
103
|
+
container_json = container.json
|
104
|
+
container_name = container_json["Name"]
|
105
|
+
@active_container_names << container_name
|
106
|
+
end
|
107
|
+
|
108
|
+
instance_index = 1
|
109
|
+
container_name_taken = true
|
110
|
+
while (true)
|
111
|
+
instance_ext = "_#{instance_index}"
|
112
|
+
instance_name = "/#{run_name}#{instance_ext}"
|
113
|
+
if (!@active_container_names.include? instance_name)
|
114
|
+
@instance_index = instance_index
|
115
|
+
break
|
116
|
+
else
|
117
|
+
instance_index += 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
instance_ext = ""
|
123
|
+
instance_ext = "_#{@instance_index}" if (@use_extension)
|
124
|
+
instance_name = "#{run_name}#{instance_ext}"
|
125
|
+
|
126
|
+
@all_docker_apis_in_cluster.each do |docker_api|
|
127
|
+
kill_previous_containers_in_cluster(docker_api, instance_name)
|
128
|
+
end
|
129
|
+
image_name = image_name_with_registry()
|
130
|
+
|
131
|
+
@run_on_apis.each do |docker_api|
|
132
|
+
if (!@run_untagged)
|
133
|
+
pull_image_to_host(docker_api, registry)
|
134
|
+
end
|
135
|
+
run_container(docker_api, instance_name)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def pull_image_to_host(docker_api, registry)
|
143
|
+
return if (registry == nil) || (@run_untagged)
|
144
|
+
Excon.defaults[:write_timeout] = 500
|
145
|
+
Excon.defaults[:read_timeout] = 500
|
146
|
+
options = {}
|
147
|
+
options[:repo] = registry
|
148
|
+
options[:fromImage] = "#{image_name_with_registry}:#{image_version}"
|
149
|
+
pp "See if Host needs to pull image (#{options[:fromImage]}). Host: #{docker_api.connection}"
|
150
|
+
|
151
|
+
image = Docker::Image.create(options, nil, docker_api.connection)
|
152
|
+
end
|
153
|
+
|
154
|
+
def run_container(docker_api, instance_name)
|
155
|
+
create_options = @run_config['options'] || {}
|
156
|
+
|
157
|
+
if (!image_name_with_registry.include? ":")
|
158
|
+
create_options['Image'] = "#{image_name_with_registry}:#{image_version}"
|
159
|
+
else
|
160
|
+
create_options['Image'] = image_name_with_registry
|
161
|
+
end
|
162
|
+
create_options['name'] = instance_name
|
163
|
+
create_options['Labels'] = { deployer: @user, deploy_id: @deployment_id }
|
164
|
+
create_options['Tty'] = true if (@interact == true)
|
165
|
+
create_options['Entrypoint'] = ["/bin/bash"] if (@interact == true)
|
166
|
+
|
167
|
+
pp "Running container: #{instance_name}\nOptions: #{create_options}"
|
168
|
+
cli_command = "docker run --name #{instance_name}"
|
169
|
+
if (create_options['Env'] != nil)
|
170
|
+
create_options['Env'].each do |env|
|
171
|
+
cli_command += " -e #{env}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
if (create_options['HostConfig'] != nil)
|
175
|
+
host_config = create_options['HostConfig']
|
176
|
+
binds = host_config['Binds']
|
177
|
+
if (binds != nil)
|
178
|
+
binds.each do |bind|
|
179
|
+
cli_command += " -v #{bind}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
links = host_config['Links']
|
183
|
+
if (links != nil)
|
184
|
+
links.each do |link|
|
185
|
+
cli_command += " --link #{link}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
ports = host_config['PortBindings']
|
189
|
+
ports.keys.each do |port|
|
190
|
+
container_port = port.split("/").first
|
191
|
+
host_port = container_port
|
192
|
+
port_binds = ports[port]
|
193
|
+
port_binds.each do |bind|
|
194
|
+
host_port = bind['HostPort'] if (bind['HostPort'] != nil)
|
195
|
+
cli_command += " -p #{host_port}:#{container_port}"
|
196
|
+
end
|
197
|
+
if (port_binds.length == 0)
|
198
|
+
cli_command += " -p #{host_port}:#{container_port}"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
cli_command += " -d"
|
203
|
+
cli_command += " #{create_options['Image']}"
|
204
|
+
cli_command += " #{create_options['Entrypoint']}" if (create_options['Entrypoint'] != nil)
|
205
|
+
puts "CLI Command: #{cli_command}"
|
206
|
+
|
207
|
+
@api_container = Docker::Container.create(create_options, docker_api.connection)
|
208
|
+
puts @api_container.start
|
209
|
+
if (@interact)
|
210
|
+
pp "=================================================================="
|
211
|
+
pp "CONTAINER STARTED FOR INTERACTION - ENTRYPOINT NOT CALLED"
|
212
|
+
pp "To attach: docker exec -it '#{instance_name}' bash"
|
213
|
+
pp "=================================================================="
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
def kill_previous_containers_in_cluster(docker_api, instance_name)
|
219
|
+
pp "Killing and removing containers with same name: #{instance_name}"
|
220
|
+
@api_container = nil
|
221
|
+
begin
|
222
|
+
@api_container = Docker::Container.get(instance_name, {}, docker_api.connection)
|
223
|
+
puts "Killing and removing container: #{instance_name} in #{docker_api.connection}"
|
224
|
+
@api_container.kill()
|
225
|
+
rescue Docker::Error::NotFoundError => not_found_error
|
226
|
+
puts "No container with name \"#{instance_name}\" running. No need to kill."
|
227
|
+
end
|
228
|
+
|
229
|
+
if (@api_container)
|
230
|
+
begin
|
231
|
+
pp "Removing container: #{instance_name}"
|
232
|
+
@api_container.remove() if (@api_container != nil)
|
233
|
+
rescue Docker::Error::NotFoundError => remove_error
|
234
|
+
puts "Didn't find image to remove: #{instance_name}"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
def image_name_with_registry
|
241
|
+
image_name = @image_name
|
242
|
+
if (registry != nil) && (!@run_untagged)
|
243
|
+
registry_without_protocol = registry.split("://").last
|
244
|
+
image_name = "#{registry_without_protocol}/#{@image_name}"
|
245
|
+
end
|
246
|
+
return image_name
|
247
|
+
end
|
248
|
+
|
249
|
+
def container_state
|
250
|
+
return @api_container.json['State']
|
251
|
+
end
|
252
|
+
|
253
|
+
def wait_for_completion
|
254
|
+
while (container_state()["Running"] == true)
|
255
|
+
sleep 1
|
256
|
+
puts "Waiting for container #{run_name} to finish."
|
257
|
+
end
|
258
|
+
exit_code = container_state()['ExitCode']
|
259
|
+
raise "Container failed (#{image_name_with_registry()}) Exit code: #{exit_code}" if (exit_code != 0)
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
FROM ruby:2.1.5
|
2
|
+
|
3
|
+
|
4
|
+
ENV GIT_REPO_ADDRESS <<GIT_REPO_ADDRESS>>
|
5
|
+
ENV DOCKER_IMAGE true
|
6
|
+
ENV APP_HOME /application
|
7
|
+
ENV RAILS_ENV production
|
8
|
+
|
9
|
+
|
10
|
+
RUN apt-get update && apt-get install -y --force-yes \
|
11
|
+
autoconf \
|
12
|
+
build-essential \
|
13
|
+
cmake \
|
14
|
+
pkg-config \
|
15
|
+
libssl-dev \
|
16
|
+
libyaml-dev \
|
17
|
+
libreadline6-dev \
|
18
|
+
zlib1g-dev \
|
19
|
+
libffi-dev \
|
20
|
+
libncurses5-dev \
|
21
|
+
libgdbm3 \
|
22
|
+
libgdbm-dev \
|
23
|
+
libsqlite3-dev \
|
24
|
+
libmysqlclient-dev \
|
25
|
+
libv8-dev \
|
26
|
+
# telnet \
|
27
|
+
mysql-client \
|
28
|
+
git \
|
29
|
+
netcat \
|
30
|
+
wget && \
|
31
|
+
gem install --no-document bundler && \
|
32
|
+
echo "Set up ssh credentials for gitlab, so we can clone olex at docker image run time." && \
|
33
|
+
mkdir /root/.ssh
|
34
|
+
|
35
|
+
|
36
|
+
# COPY files/rails_application.tar.gz /application
|
37
|
+
COPY files/ssh/* /root/.ssh/
|
38
|
+
|
39
|
+
RUN echo "RUNNING CONTAINER ENTRYPOINT" && \
|
40
|
+
rm -rf "$APP_HOME" && \
|
41
|
+
git clone "$GIT_REPO_ADDRESS" "$APP_HOME" &&\
|
42
|
+
cd "$APP_HOME" && \
|
43
|
+
bundle install --without test development
|
44
|
+
|
45
|
+
EXPOSE 3000
|
46
|
+
|
47
|
+
# COPY entrypoint.sh /entrypoint.sh
|
48
|
+
# ENTRYPOINT ["/entrypoint.sh"]
|
49
|
+
# CMD ["/application/rails","s"]
|
@@ -0,0 +1,46 @@
|
|
1
|
+
FROM ruby:2.1.5
|
2
|
+
|
3
|
+
ENV SQL_HOST <<SQL_HOST>>
|
4
|
+
ENV SQL_HOST_PORT 3306
|
5
|
+
|
6
|
+
ENV DOCKER_IMAGE true
|
7
|
+
ENV APP_HOME /application
|
8
|
+
ENV RAILS_ENV production
|
9
|
+
|
10
|
+
|
11
|
+
RUN apt-get update && apt-get install -y --force-yes \
|
12
|
+
autoconf \
|
13
|
+
build-essential \
|
14
|
+
cmake \
|
15
|
+
pkg-config \
|
16
|
+
libssl-dev \
|
17
|
+
libyaml-dev \
|
18
|
+
libreadline6-dev \
|
19
|
+
zlib1g-dev \
|
20
|
+
libffi-dev \
|
21
|
+
libncurses5-dev \
|
22
|
+
libgdbm3 \
|
23
|
+
libgdbm-dev \
|
24
|
+
libsqlite3-dev \
|
25
|
+
libmysqlclient-dev \
|
26
|
+
libv8-dev \
|
27
|
+
mysql-client \
|
28
|
+
netcat \
|
29
|
+
wget && \
|
30
|
+
gem install --no-document bundler &&\
|
31
|
+
mkdir -p "$APP_HOME" &&\
|
32
|
+
gem install puma
|
33
|
+
|
34
|
+
COPY files/rails_application.tar.gz "$APP_HOME"
|
35
|
+
|
36
|
+
RUN echo "PREPARING APPLICATION" && \
|
37
|
+
cd "$APP_HOME" &&\
|
38
|
+
tar xzf rails_application.tar.gz && \
|
39
|
+
bundle install --without test development
|
40
|
+
|
41
|
+
COPY files/database.yml "$APP_HOME/config"
|
42
|
+
|
43
|
+
EXPOSE 3000
|
44
|
+
COPY wait_for_port.sh /wait_for_port.sh
|
45
|
+
COPY entrypoint.sh /entrypoint.sh
|
46
|
+
ENTRYPOINT ["/entrypoint.sh"]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
cd "$APP_HOME"
|
3
|
+
|
4
|
+
echo "================ Waiting for MySQL's port to respond"
|
5
|
+
|
6
|
+
/bin/bash /wait_for_port.sh "$SQL_HOST" "$SQL_HOST_PORT"
|
7
|
+
|
8
|
+
echo "================ Migrating Openlogic database"
|
9
|
+
rake db:create db:migrate
|
10
|
+
|
11
|
+
# echo "================ Starting memcache - oh no, two long running processes in the same container."
|
12
|
+
# memcached -d -uroot
|
13
|
+
|
14
|
+
|
15
|
+
rails s -d Puma -b 0.0.0.0 -p 3000
|
16
|
+
tail -100f ./log/production.log
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class InitializeProject
|
4
|
+
def app_docker_path
|
5
|
+
project_dir = FileUtils.pwd
|
6
|
+
docker_dir = "#{project_dir}/docker"
|
7
|
+
FileUtils.mkdir_p("#{docker_dir}/files")
|
8
|
+
return docker_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def fill_in_file_param(file_path, param_name, param_value)
|
12
|
+
new_content = nil
|
13
|
+
File.open(file_path, "r") do |file|
|
14
|
+
content = file.read
|
15
|
+
search_str = "<<#{param_name}>>"
|
16
|
+
index = content.index(search_str)
|
17
|
+
content_start = content[0..(index - 1)]
|
18
|
+
content_end = content[(index + search_str.length), content.length]
|
19
|
+
new_content = content_start + param_value + content_end
|
20
|
+
end
|
21
|
+
File.open(file_path, "w") do |file|
|
22
|
+
file.write(new_content)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_configuration
|
27
|
+
#Use directory name as default for image name.
|
28
|
+
default_image_name = FileUtils.pwd.split('/').last
|
29
|
+
default_image_name = default_image_name.split('_').join("-")
|
30
|
+
default_image_name = default_image_name.split(' ').join("-")
|
31
|
+
|
32
|
+
puts "Creating configurations."
|
33
|
+
puts "Enter image name of rails app (#{default_image_name}):"
|
34
|
+
image_name = STDIN.gets.chomp
|
35
|
+
if (image_name.length == 0)
|
36
|
+
image_name = default_image_name
|
37
|
+
end
|
38
|
+
|
39
|
+
db_run_time_name = image_name + "-db"
|
40
|
+
|
41
|
+
docker_dir = app_docker_path()
|
42
|
+
FileUtils.mkdir_p("#{docker_dir}/files")
|
43
|
+
|
44
|
+
this_dir = File.dirname(__FILE__)
|
45
|
+
docker_file_path = "#{docker_dir}/Dockerfile"
|
46
|
+
copy_docker_file = true
|
47
|
+
if (File.exist?(docker_file_path))
|
48
|
+
puts "Dockerfile already exists in #{docker_dir}. Overwrite? (y/n)"
|
49
|
+
overwrite = STDIN.gets.chomp
|
50
|
+
if (overwrite.downcase == "n")
|
51
|
+
copy_docker_file = false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if (copy_docker_file)
|
56
|
+
FileUtils.cp("#{this_dir}/image_files/DockerfileSelf", docker_file_path)
|
57
|
+
fill_in_file_param(docker_file_path, 'SQL_HOST', db_run_time_name)
|
58
|
+
puts "Copied standard Dockerfile to: #{docker_dir}"
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
copy_entry_file = true
|
63
|
+
entry_file_path = "#{docker_dir}/entrypoint.sh"
|
64
|
+
if (File.exist?(entry_file_path))
|
65
|
+
puts "Entrypoint.sh already exists in #{docker_dir}. Overwrite? (y/n)"
|
66
|
+
overwrite = STDIN.gets.chomp
|
67
|
+
if (overwrite.downcase == "n")
|
68
|
+
copy_entry_file = false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if (copy_entry_file)
|
73
|
+
FileUtils.cp("#{this_dir}/image_files/entrypoint.sh", "#{docker_dir}/entrypoint.sh")
|
74
|
+
puts "Copied entry point file to: #{docker_dir}"
|
75
|
+
end
|
76
|
+
|
77
|
+
FileUtils.cp("#{this_dir}/image_files/wait_for_port.sh", "#{docker_dir}/wait_for_port.sh")
|
78
|
+
|
79
|
+
|
80
|
+
main_configuration = {
|
81
|
+
registry: "https://my.registry.com:5000",
|
82
|
+
build: {
|
83
|
+
name: image_name
|
84
|
+
},
|
85
|
+
run: {
|
86
|
+
name: image_name,
|
87
|
+
options: {
|
88
|
+
"Env" => [
|
89
|
+
"IS_DOCKER=true"
|
90
|
+
],
|
91
|
+
"HostConfig" => {
|
92
|
+
"Links" => ["#{db_run_time_name}:#{db_run_time_name}"],
|
93
|
+
"PortBindings" => {
|
94
|
+
"3000/tcp" => [
|
95
|
+
{"HostIp" => "", "HostPort" => "3000"}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
sql_configuration = {
|
104
|
+
run: {
|
105
|
+
name: db_run_time_name,
|
106
|
+
image_name: "percona:5.6",
|
107
|
+
options: {
|
108
|
+
"Env" => [
|
109
|
+
"MYSQL_ROOT_PASSWORD=password"
|
110
|
+
],
|
111
|
+
"HostConfig" => {
|
112
|
+
"PortBindings" => {
|
113
|
+
"3306/tcp" => []
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
|
121
|
+
configs = [{sql: sql_configuration}, {rails_app: main_configuration}]
|
122
|
+
|
123
|
+
output = JSON.pretty_generate(configs)
|
124
|
+
config_file_path = "#{app_docker_path}/docker-rails.json"
|
125
|
+
File.open(config_file_path, "w") do |f|
|
126
|
+
f.write(output)
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module LaunchSupport
|
2
|
+
|
3
|
+
def is_port_open?(ip, port)
|
4
|
+
begin
|
5
|
+
Timeout::timeout(1) do
|
6
|
+
begin
|
7
|
+
puts "Connecting to #{ip}: #{port}"
|
8
|
+
s = TCPSocket.new(ip, port)
|
9
|
+
s.close
|
10
|
+
return true
|
11
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
12
|
+
return false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rescue Timeout::Error
|
16
|
+
end
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def wait_for_short_lived_image_to_complete(image_run_name)
|
22
|
+
done = false
|
23
|
+
while (!done)
|
24
|
+
#puts "Inspecting image: #{image_run_name}"
|
25
|
+
output = `docker inspect #{image_run_name}`
|
26
|
+
#puts "Output: #{output}"
|
27
|
+
json = JSON.parse(output)
|
28
|
+
state = json.first['State']
|
29
|
+
is_running = state['Running']
|
30
|
+
exit_code = state['ExitCode']
|
31
|
+
puts "Waiting for short-lived container to complete: \"#{image_run_name}\""
|
32
|
+
if (exit_code == 0) && (is_running == false)
|
33
|
+
done = true
|
34
|
+
elsif (exit_code != 0)
|
35
|
+
raise "Container failed: #{image_run_name} - Run: docker logs -f #{image_run_name}"
|
36
|
+
else
|
37
|
+
sleep 3
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def wait_for_port(ip, port)
|
43
|
+
while (!is_port_open?(ip, port))
|
44
|
+
puts "Port not ready: #{ip}:#{port}"
|
45
|
+
sleep 3
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def host
|
51
|
+
output = `boot2docker ip`
|
52
|
+
if (output.strip.length == 0) || (output.include?("command not found")) || (output.include?("is not running"))
|
53
|
+
docker_machine_active = `docker-machine active`
|
54
|
+
docker_machine_active = docker_machine_active[0..(docker_machine_active.length - 2)]
|
55
|
+
output = `docker-machine url #{docker_machine_active}`
|
56
|
+
address = output.split("//")[1].split(":").first
|
57
|
+
return address
|
58
|
+
else
|
59
|
+
return output.strip
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative "../support/docker_rails_config_reader"
|
2
|
+
require_relative "../support/docker_rails_app_creator"
|
3
|
+
|
4
|
+
|
5
|
+
namespace :docker do
|
6
|
+
|
7
|
+
|
8
|
+
desc "Build docker image of this project"
|
9
|
+
task :build => :environment do
|
10
|
+
config_reader = DockerRailsConfigReader.new
|
11
|
+
config_reader.definitions.each do |definition|
|
12
|
+
definition_name = definition.keys.first
|
13
|
+
definition_value = definition[definition_name]
|
14
|
+
if (definition_value['build'])
|
15
|
+
builder = DockerRailsAppCreator.new(definition_value)
|
16
|
+
builder.docker_host = "tcp://127.0.0.1:2375"
|
17
|
+
builder.docker_host_secure = true
|
18
|
+
builder.build_it
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Push docker image to repo"
|
24
|
+
task :push => :environment do
|
25
|
+
config_reader = DockerRailsConfigReader.new
|
26
|
+
config_reader.definitions.each do |definition|
|
27
|
+
definition_name = definition.keys.first
|
28
|
+
definition_value = definition[definition_name]
|
29
|
+
if (definition_value['build'])
|
30
|
+
builder = DockerRailsAppCreator.new(definition_value)
|
31
|
+
builder.docker_host = "tcp://127.0.0.1:2375"
|
32
|
+
builder.docker_host_secure = true
|
33
|
+
registry = definition_value['registry']
|
34
|
+
if (registry)
|
35
|
+
builder.tag_it(registry)
|
36
|
+
builder.push_it(registry)
|
37
|
+
else
|
38
|
+
puts "No registry definited. Ignoring push command."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "../support/docker_rails_app_creator"
|
2
|
+
require_relative "../support/initialize_project"
|
3
|
+
|
4
|
+
|
5
|
+
namespace :docker do
|
6
|
+
|
7
|
+
desc "Build docker image of this project"
|
8
|
+
task :install => :environment do
|
9
|
+
initializer = InitializeProject.new
|
10
|
+
initializer.create_configuration()
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/lib/tasks/run.rake
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative "../support/docker_rails_app_creator"
|
2
|
+
|
3
|
+
namespace :docker do
|
4
|
+
|
5
|
+
desc "Run Docker image"
|
6
|
+
task :run => :environment do
|
7
|
+
config_reader = DockerRailsConfigReader.new
|
8
|
+
config_reader.definitions.each do |definition|
|
9
|
+
definition_name = definition.keys.first
|
10
|
+
definition_value = definition[definition_name]
|
11
|
+
if (definition_value['run'])
|
12
|
+
docker_runner = DockerRunner.new(definition_value)
|
13
|
+
docker_runner.docker_host = "tcp://127.0.0.1:2375"
|
14
|
+
docker_runner.docker_host_secure = true
|
15
|
+
docker_runner.run
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
desc "Run Local Docker image"
|
22
|
+
task :run_local => :environment do
|
23
|
+
config_reader = DockerRailsConfigReader.new
|
24
|
+
config_reader.definitions.each do |definition|
|
25
|
+
definition_name = definition.keys.first
|
26
|
+
definition_value = definition[definition_name]
|
27
|
+
if (definition_value['run'])
|
28
|
+
docker_runner = DockerRunner.new(definition_value)
|
29
|
+
docker_runner.docker_host = "tcp://127.0.0.1:2375"
|
30
|
+
docker_runner.docker_host_secure = true
|
31
|
+
docker_runner.run_untagged = true
|
32
|
+
docker_runner.run
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: docker-rails-app
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Moore
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: docker-api
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.31'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.31'
|
55
|
+
description: Puts your rails app into a Docker image
|
56
|
+
email:
|
57
|
+
- m.moore.denver@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- bin/console
|
63
|
+
- bin/setup
|
64
|
+
- lib/docker_rails_app.rb
|
65
|
+
- lib/docker_rails_app/import_gem_rake_tasks.rb
|
66
|
+
- lib/docker_rails_app/version.rb
|
67
|
+
- lib/support/docker_api.rb
|
68
|
+
- lib/support/docker_image_creator.rb
|
69
|
+
- lib/support/docker_rails_app_creator.rb
|
70
|
+
- lib/support/docker_rails_config_reader.rb
|
71
|
+
- lib/support/docker_registry.rb
|
72
|
+
- lib/support/docker_runner.rb
|
73
|
+
- lib/support/image_files/DockerfileRepo
|
74
|
+
- lib/support/image_files/DockerfileSelf
|
75
|
+
- lib/support/image_files/entrypoint.sh
|
76
|
+
- lib/support/image_files/wait_for_file.sh
|
77
|
+
- lib/support/image_files/wait_for_port.sh
|
78
|
+
- lib/support/initialize_project.rb
|
79
|
+
- lib/support/launch_support.rb
|
80
|
+
- lib/tasks/deploy.rake
|
81
|
+
- lib/tasks/initialize.rake
|
82
|
+
- lib/tasks/run.rake
|
83
|
+
homepage:
|
84
|
+
licenses:
|
85
|
+
- MIT
|
86
|
+
metadata: {}
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
- lib/docker_rails_app
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.4.5.1
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Puts your rails app into a Docker image
|
108
|
+
test_files: []
|