docker_toolkit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.dockerignore +20 -0
- data/.gitignore +109 -0
- data/.rspec +4 -0
- data/.rubocop.yml +89 -0
- data/.travis.yml +12 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +59 -0
- data/README.md +4 -0
- data/bin/consul.rb +222 -0
- data/bin/merger.rb +116 -0
- data/bin/waiter.rb +246 -0
- data/docker/Dockerfile +53 -0
- data/docker/docker-compose.yml +12 -0
- data/docker/down.sh +4 -0
- data/docker/run_tests.sh +17 -0
- data/docker_toolkit.gemspec +33 -0
- data/lib/docker_toolkit/version.rb +5 -0
- data/lib/docker_toolkit.rb +10 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8bed7bfd3321be850470d15839c3e4516c030ef2fd85a5cb893719e69651235c
|
4
|
+
data.tar.gz: bca92e12468219f060491956acc7d1867c198e43b9f933a08351ee0929fd5ffd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5146180b05f18e8ba0a42da96465c3aefaf3a671b3d9b396dabb184eda42023c86df8ba6320f279962711f743eb16ea7e4fdfa781ac4caa60065199d1dea8ebd
|
7
|
+
data.tar.gz: a6776d22bbc35be6b0283cb0ee730c8eb9addb16063e8acdb3d8258d95f75f78ebb13fa62178f46a25f3744e46d9ebe187074556882d1d74fe71aadafd1f97ee
|
data/.dockerignore
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
6
|
+
|
7
|
+
# bundler state
|
8
|
+
/.bundle
|
9
|
+
vendor/cache
|
10
|
+
vendor/bundle
|
11
|
+
/vendor/ruby/
|
12
|
+
|
13
|
+
# minimal Rails specific artifacts
|
14
|
+
db/*.sqlite3
|
15
|
+
/db/*.sqlite3-journal
|
16
|
+
/log/*
|
17
|
+
/tmp/*
|
18
|
+
|
19
|
+
# add /config/database.yml if it contains passwords
|
20
|
+
# /config/database.yml
|
21
|
+
|
22
|
+
# various artifacts
|
23
|
+
**.war
|
24
|
+
*.rbc
|
25
|
+
*.sassc
|
26
|
+
.redcar/
|
27
|
+
.sass-cache
|
28
|
+
/config/config.yml
|
29
|
+
/coverage.data
|
30
|
+
/coverage/
|
31
|
+
/db/*.javadb/
|
32
|
+
/db/*.sqlite3
|
33
|
+
/config/database.yml
|
34
|
+
/config/secrets.yml
|
35
|
+
oxymoron.js
|
36
|
+
/doc/api/
|
37
|
+
/doc/app/
|
38
|
+
/doc/features.html
|
39
|
+
/doc/specs.html
|
40
|
+
/public/cache
|
41
|
+
/public/uploads
|
42
|
+
/public/stylesheets/compiled
|
43
|
+
/public/system/*
|
44
|
+
/spec/tmp/*
|
45
|
+
/cache
|
46
|
+
/capybara*
|
47
|
+
/capybara-*.html
|
48
|
+
/gems
|
49
|
+
/specifications
|
50
|
+
app/assets/fonts/disp-preview.html
|
51
|
+
rerun.txt
|
52
|
+
pickle-email-*.html
|
53
|
+
*.sublime-project
|
54
|
+
*.sublime-workspace
|
55
|
+
.zeus.sock
|
56
|
+
.byebug_history
|
57
|
+
|
58
|
+
#test
|
59
|
+
/docs
|
60
|
+
|
61
|
+
|
62
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
63
|
+
# or operating system, you probably want to add a global ignore instead:
|
64
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
65
|
+
#
|
66
|
+
# Here are some files you may want to ignore globally:
|
67
|
+
|
68
|
+
# scm revert files
|
69
|
+
**.orig
|
70
|
+
|
71
|
+
# Mac finder artifacts
|
72
|
+
.DS_Store
|
73
|
+
|
74
|
+
# Netbeans project directory
|
75
|
+
/nbproject/
|
76
|
+
|
77
|
+
# RubyMine project files
|
78
|
+
.idea
|
79
|
+
|
80
|
+
# Textmate project files
|
81
|
+
/*.tmproj
|
82
|
+
|
83
|
+
# vim artifacts
|
84
|
+
**.swp
|
85
|
+
|
86
|
+
# Environment files that may contain sensitive data
|
87
|
+
.env
|
88
|
+
.powenv
|
89
|
+
|
90
|
+
# tilde files are usually backup files from a text editor
|
91
|
+
*~
|
92
|
+
public/assets/
|
93
|
+
config/keys
|
94
|
+
.ruby-gemset
|
95
|
+
.ruby-version
|
96
|
+
spec/reports/
|
97
|
+
app/assets/javascripts/oxymoron.js
|
98
|
+
|
99
|
+
!/log/.keep
|
100
|
+
!/tmp/.keep
|
101
|
+
|
102
|
+
/node_modules
|
103
|
+
/yarn-error.log
|
104
|
+
|
105
|
+
.byebug_history
|
106
|
+
/public/packs
|
107
|
+
/public/packs-test
|
108
|
+
/node_modules
|
109
|
+
passenger.*.pid.lock
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.5
|
3
|
+
Exclude:
|
4
|
+
- 'storage/**/*'
|
5
|
+
- 'docker/**/*'
|
6
|
+
|
7
|
+
Style/AsciiComments:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/RedundantBegin:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/GlobalVars:
|
14
|
+
AllowedVariables: ['$logger', '$root']
|
15
|
+
|
16
|
+
Metrics/BlockLength:
|
17
|
+
Exclude:
|
18
|
+
- 'spec/**/*.rb'
|
19
|
+
|
20
|
+
Metrics/MethodLength:
|
21
|
+
Max: 20
|
22
|
+
|
23
|
+
Style/ClassAndModuleChildren:
|
24
|
+
EnforcedStyle: compact
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/Documentation:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/Lambda:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
|
34
|
+
Style/RaiseArgs:
|
35
|
+
EnforcedStyle: compact
|
36
|
+
|
37
|
+
Metrics/LineLength:
|
38
|
+
Max: 100
|
39
|
+
|
40
|
+
#Layout/IndentationWidth:
|
41
|
+
# Enabled: true
|
42
|
+
|
43
|
+
#Layout/IndentAssignment:
|
44
|
+
# Enabled: false
|
45
|
+
# IndentationWidth: false
|
46
|
+
|
47
|
+
#Layout/ElseAlignment:
|
48
|
+
# Enabled: false
|
49
|
+
|
50
|
+
#Layout/EndAlignment:
|
51
|
+
# Enabled: false
|
52
|
+
|
53
|
+
#Lint/AssignmentInCondition:
|
54
|
+
# Enabled: false
|
55
|
+
|
56
|
+
Layout/IndentationConsistency:
|
57
|
+
EnforcedStyle: rails
|
58
|
+
|
59
|
+
Layout/EmptyLines:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
Layout/EmptyLinesAroundClassBody:
|
63
|
+
EnforcedStyle: empty_lines
|
64
|
+
|
65
|
+
Layout/EmptyLinesAroundModuleBody:
|
66
|
+
EnforcedStyle: empty_lines
|
67
|
+
|
68
|
+
Layout/SpaceInsideBlockBraces:
|
69
|
+
EnforcedStyle: space
|
70
|
+
SpaceBeforeBlockParameters: false
|
71
|
+
|
72
|
+
Layout/SpaceAroundBlockParameters:
|
73
|
+
EnforcedStyleInsidePipes: no_space
|
74
|
+
|
75
|
+
Layout/SpaceBeforeBlockBraces:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
Style/NumericPredicate:
|
79
|
+
Enabled: false
|
80
|
+
|
81
|
+
Style/FrozenStringLiteralComment:
|
82
|
+
Enabled: false
|
83
|
+
|
84
|
+
Style/DoubleNegation:
|
85
|
+
Enabled: false
|
86
|
+
|
87
|
+
Style/SymbolArray:
|
88
|
+
Enabled: false
|
89
|
+
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
docker_toolkit (0.1.0)
|
5
|
+
diplomat
|
6
|
+
json
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
awesome_print (1.8.0)
|
12
|
+
concurrent-ruby (1.0.5)
|
13
|
+
diff-lcs (1.3)
|
14
|
+
diplomat (2.0.2)
|
15
|
+
faraday (~> 0.9)
|
16
|
+
json
|
17
|
+
faker (1.9.1)
|
18
|
+
i18n (>= 0.7)
|
19
|
+
faker-russian (0.0.6)
|
20
|
+
faraday (0.15.2)
|
21
|
+
multipart-post (>= 1.2, < 3)
|
22
|
+
i18n (1.0.1)
|
23
|
+
concurrent-ruby (~> 1.0)
|
24
|
+
json (2.1.0)
|
25
|
+
multipart-post (2.0.0)
|
26
|
+
rake (10.5.0)
|
27
|
+
rspec (3.7.0)
|
28
|
+
rspec-core (~> 3.7.0)
|
29
|
+
rspec-expectations (~> 3.7.0)
|
30
|
+
rspec-mocks (~> 3.7.0)
|
31
|
+
rspec-core (3.7.1)
|
32
|
+
rspec-support (~> 3.7.0)
|
33
|
+
rspec-expectations (3.7.0)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.7.0)
|
36
|
+
rspec-mocks (3.7.0)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.7.0)
|
39
|
+
rspec-retry (0.6.1)
|
40
|
+
rspec-core (> 3.3)
|
41
|
+
rspec-set (0.1.3)
|
42
|
+
rspec-support (3.7.1)
|
43
|
+
|
44
|
+
PLATFORMS
|
45
|
+
ruby
|
46
|
+
|
47
|
+
DEPENDENCIES
|
48
|
+
awesome_print
|
49
|
+
bundler (~> 1.14)
|
50
|
+
docker_toolkit!
|
51
|
+
faker
|
52
|
+
faker-russian
|
53
|
+
rake (~> 10.0)
|
54
|
+
rspec
|
55
|
+
rspec-retry
|
56
|
+
rspec-set
|
57
|
+
|
58
|
+
BUNDLED WITH
|
59
|
+
1.16.1
|
data/README.md
ADDED
data/bin/consul.rb
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'diplomat'
|
4
|
+
require 'optparse'
|
5
|
+
require 'English'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
STDOUT.sync = true
|
9
|
+
STDERR.sync = true
|
10
|
+
|
11
|
+
@opts = {}
|
12
|
+
|
13
|
+
@opts[:exec] = (begin
|
14
|
+
ARGV.join(' ').split(' -- ')[1].strip
|
15
|
+
rescue StandardError
|
16
|
+
nil
|
17
|
+
end)
|
18
|
+
|
19
|
+
parser = OptionParser.new do |o|
|
20
|
+
o.banner = 'Usage: consul.rb [options] -- exec'
|
21
|
+
|
22
|
+
o.on('--consul url', 'Set up a custom Consul URL') do |url|
|
23
|
+
Diplomat.configure do |config|
|
24
|
+
config.url = url.strip
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
o.on('--token token', 'Connect into consul with custom access token (ACL)') do |token|
|
29
|
+
Diplomat.configure do |config|
|
30
|
+
config.acl_token = token.strip
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
o.on('--init [service]', 'Initialize Consul services from config') do |service|
|
35
|
+
@opts[:service] = service
|
36
|
+
@opts[:init] = true
|
37
|
+
end
|
38
|
+
|
39
|
+
o.on('--config file', 'Read service configulation from file') do |file|
|
40
|
+
@opts[:config] = file.strip
|
41
|
+
end
|
42
|
+
|
43
|
+
o.on('--upload', 'Upload files to variables') do
|
44
|
+
@opts[:upload] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
o.on('--show [service]', 'Show service configulation from Consul') do |service|
|
48
|
+
@opts[:service] = service
|
49
|
+
@opts[:show] = true
|
50
|
+
end
|
51
|
+
|
52
|
+
o.on('--override', 'override existed keys') do
|
53
|
+
@opts[:override] = true
|
54
|
+
end
|
55
|
+
|
56
|
+
o.on('-d', '--dereference', 'dereference consul values in form of "consul://key/subkey"') do
|
57
|
+
@opts[:dereference] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
o.on('--env prefix', 'export KV values from prefix as env varaibles') do |prefix|
|
61
|
+
@opts[:env] = (prefix + '/').gsub('//', '/')
|
62
|
+
end
|
63
|
+
|
64
|
+
o.on('--export', 'add export to --env output') do
|
65
|
+
@opts[:export] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
o.on('--pristine', "not include the parent processes' environment when exec child process") do
|
69
|
+
@opts[:pristine] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
o.on('--put path:value', 'put value to path') do |path|
|
73
|
+
@opts[:put] = path.strip
|
74
|
+
end
|
75
|
+
|
76
|
+
o.on('--get path', 'get value from') do |path|
|
77
|
+
@opts[:get] = path.strip
|
78
|
+
end
|
79
|
+
end
|
80
|
+
parser.parse!
|
81
|
+
|
82
|
+
def die(message)
|
83
|
+
STDERR.puts "Error: #{message}"
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
|
87
|
+
def key_to_consul(key)
|
88
|
+
key.downcase.gsub(/[^0-9a-z]/i, '_')
|
89
|
+
end
|
90
|
+
|
91
|
+
def key_to_env(key)
|
92
|
+
key.upcase.gsub(/[^0-9a-z]/i, '_')
|
93
|
+
end
|
94
|
+
|
95
|
+
def dereferenced_value(value)
|
96
|
+
if @opts[:dereference] && value && value[/^consul:\/\//]
|
97
|
+
reference_path = value.gsub(/^consul:\/\//, '')
|
98
|
+
dereferenced_value(Diplomat::Kv.get(reference_path))
|
99
|
+
else
|
100
|
+
value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
if config = @opts[:config]
|
105
|
+
@opts[:config] = YAML.safe_load(config == '-' ? STDIN.read : File.read(config), [], [], true)
|
106
|
+
end
|
107
|
+
|
108
|
+
if @opts[:init]
|
109
|
+
raise OptionParser::MissingArgument.new('config') unless @opts[:config]
|
110
|
+
|
111
|
+
services = if service = @opts[:service]
|
112
|
+
{
|
113
|
+
service => @opts[:config][service]
|
114
|
+
}
|
115
|
+
else
|
116
|
+
@opts[:config]
|
117
|
+
end
|
118
|
+
|
119
|
+
services.each_pair do |service, config|
|
120
|
+
next unless config
|
121
|
+
next if service[/^\./] # skip hidden keys
|
122
|
+
|
123
|
+
path = "services/env/#{service}"
|
124
|
+
config.each_pair do |env, item|
|
125
|
+
key = "#{path}/#{key_to_consul(env)}"
|
126
|
+
value = if @opts[:upload] && item['file']
|
127
|
+
File.read(item['file'])
|
128
|
+
else
|
129
|
+
item['value'] || item['default'] || item['file']
|
130
|
+
end
|
131
|
+
|
132
|
+
empty = begin
|
133
|
+
Diplomat::Kv.get(key)
|
134
|
+
false
|
135
|
+
rescue Diplomat::KeyNotFound
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
Diplomat::Kv.put(key, value.to_s.strip) || die("Can't put #{key} to Consul") if empty || @opts[:override]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
exit 0
|
144
|
+
end
|
145
|
+
|
146
|
+
if @opts[:show]
|
147
|
+
config = {}
|
148
|
+
|
149
|
+
path = if service = @opts[:service]
|
150
|
+
"services/env/#{service}/"
|
151
|
+
else
|
152
|
+
'services/env/'
|
153
|
+
end
|
154
|
+
|
155
|
+
answer = Diplomat::Kv.get(path, recurse: true, convert_to_hash: true) || die("Can't get #{path} from Consul")
|
156
|
+
answer['services']['env'].each_pair do |service, env|
|
157
|
+
cfg = config[service] ||= {}
|
158
|
+
|
159
|
+
env.each_pair do |key, value|
|
160
|
+
value = dereferenced_value(value)
|
161
|
+
|
162
|
+
cfg[key_to_env(key)] = {
|
163
|
+
env: key_to_env(key),
|
164
|
+
value: value
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
STDOUT.puts JSON.parse(config.to_json).to_yaml
|
170
|
+
|
171
|
+
exit 0
|
172
|
+
end
|
173
|
+
|
174
|
+
if put = @opts[:put]
|
175
|
+
path, *value = put.split(':').map(&:strip)
|
176
|
+
value = value.join(':')
|
177
|
+
value = File.read(value) if @opts[:upload] && value && File.exist?(value)
|
178
|
+
|
179
|
+
Diplomat::Kv.put(path, value.to_s.strip) || die("Can't put #{path} to Consul")
|
180
|
+
|
181
|
+
exit 0
|
182
|
+
end
|
183
|
+
|
184
|
+
if path = @opts[:get]
|
185
|
+
value = dereferenced_value(Diplomat::Kv.get(path))
|
186
|
+
|
187
|
+
STDOUT.puts value.to_s.strip
|
188
|
+
|
189
|
+
exit 0
|
190
|
+
end
|
191
|
+
|
192
|
+
if prefix = @opts[:env]
|
193
|
+
keys = begin
|
194
|
+
Diplomat::Kv.get(prefix, keys: true)
|
195
|
+
rescue Diplomat::KeyNotFound => e
|
196
|
+
[]
|
197
|
+
rescue StandardError
|
198
|
+
die("Can't get keys at #{prefix} from Consul")
|
199
|
+
end
|
200
|
+
|
201
|
+
env = keys.reduce({}) do |e, key|
|
202
|
+
value = dereferenced_value(Diplomat::Kv.get(key))
|
203
|
+
|
204
|
+
e.merge(key_to_env(key.gsub(prefix, '')) => value)
|
205
|
+
end
|
206
|
+
|
207
|
+
if cmd = @opts[:exec]
|
208
|
+
env = ENV.to_h.merge(env) unless @opts[:pristine]
|
209
|
+
|
210
|
+
exec(env, cmd, unsetenv_others: true)
|
211
|
+
else
|
212
|
+
env.each_pair do |k, v|
|
213
|
+
STDOUT.puts "#{@opts[:export] ? 'export ' : ''}#{k}=\"#{v}\""
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
exit 0
|
219
|
+
end
|
220
|
+
|
221
|
+
STDOUT.puts parser.help
|
222
|
+
exit 1
|
data/bin/merger.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
|
7
|
+
unless ENV['COMPOSE_FILE']
|
8
|
+
STDERR.puts 'COMPOSE_FILE environment must point to one on mo files'
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
class Hash
|
15
|
+
|
16
|
+
def deep_dup
|
17
|
+
Marshal.load(Marshal.dump(self))
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Array
|
23
|
+
|
24
|
+
def deep_dup
|
25
|
+
Marshal.load(Marshal.dump(self))
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
def extend_hash(first, second)
|
33
|
+
raise ArgumentError.new('First and second args equal nil') if [first, second].all? &:nil?
|
34
|
+
return second if first.nil?
|
35
|
+
return first if second.nil?
|
36
|
+
|
37
|
+
first.each_pair do |fk, fv|
|
38
|
+
next unless second.key?(fk)
|
39
|
+
|
40
|
+
sv = second[fk]
|
41
|
+
raise "Types of values not match(#{fv.class}, #{sv.class})" if fv.class != sv.class
|
42
|
+
|
43
|
+
# Специальный случай потому что command не мерджится а заменяется
|
44
|
+
if fk == 'command'
|
45
|
+
first[fk] = sv
|
46
|
+
elsif fv.is_a? Hash
|
47
|
+
extend_hash(fv, sv)
|
48
|
+
elsif fv.is_a? Array
|
49
|
+
fv |= sv
|
50
|
+
first[fk] = fv
|
51
|
+
else
|
52
|
+
first[fk] = sv
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
second.each_pair do |sk, sv|
|
57
|
+
next if first.key?(sk)
|
58
|
+
|
59
|
+
first[sk] = sv
|
60
|
+
end
|
61
|
+
|
62
|
+
first
|
63
|
+
end
|
64
|
+
|
65
|
+
def process_compose_hash(yml, dirname, parent = {})
|
66
|
+
(yml['services'] || {}).each_pair do |name, service|
|
67
|
+
next unless ext = service['extends']
|
68
|
+
base = if ext.is_a? String
|
69
|
+
template = yml['services'][ext]
|
70
|
+
parent_service = (parent['services'] || {})[ext] || {}
|
71
|
+
extend_hash(parent_service.deep_dup, template)
|
72
|
+
elsif file = ext['file']
|
73
|
+
ENV.each_pair do |k, v|
|
74
|
+
file.gsub!("$#{k}", v)
|
75
|
+
file.gsub!("${#{k}}", v)
|
76
|
+
end
|
77
|
+
|
78
|
+
tmp = if File.exist?(dirname + '/' + file)
|
79
|
+
YAML.safe_load(File.read(dirname + '/' + file))
|
80
|
+
else
|
81
|
+
YAML.safe_load(File.read(file))
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
(tmp['services'][ext['service']] || {})
|
86
|
+
rescue StandardError
|
87
|
+
{}
|
88
|
+
end
|
89
|
+
else
|
90
|
+
yml['services'][ext['service']]
|
91
|
+
end.deep_dup
|
92
|
+
|
93
|
+
service.delete 'extends'
|
94
|
+
|
95
|
+
yml['services'][name] = extend_hash(base, service)
|
96
|
+
end
|
97
|
+
yml
|
98
|
+
end
|
99
|
+
|
100
|
+
if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
|
101
|
+
result = ENV['COMPOSE_FILE'].split(':').reduce({}) do |parent, file|
|
102
|
+
yml = process_compose_hash(YAML.safe_load(File.read(file)), File.dirname(file), parent)
|
103
|
+
if yml['version'] && parent['version'] && yml['version'] != parent['version']
|
104
|
+
raise "version mismatch: #{file}"
|
105
|
+
end
|
106
|
+
ret = extend_hash(parent.deep_dup, yml)
|
107
|
+
ret
|
108
|
+
end
|
109
|
+
|
110
|
+
if ARGV[0].nil? || ARGV[0].strip == '-'
|
111
|
+
puts YAML.dump(result)
|
112
|
+
else
|
113
|
+
File.write(ARGV[0].strip, YAML.dump(result))
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
data/bin/waiter.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'English'
|
6
|
+
require 'openssl'
|
7
|
+
require 'tempfile'
|
8
|
+
|
9
|
+
STDOUT.sync = true
|
10
|
+
STDERR.sync = true
|
11
|
+
|
12
|
+
TIMEOUT = 15
|
13
|
+
INTERVAL = 3
|
14
|
+
|
15
|
+
@opts = {}
|
16
|
+
|
17
|
+
@opts[:exec] = (begin
|
18
|
+
ARGV.join(' ').split(' -- ')[1].strip
|
19
|
+
rescue StandardError
|
20
|
+
nil
|
21
|
+
end)
|
22
|
+
@opts[:timeout] = TIMEOUT
|
23
|
+
@opts[:interval] = INTERVAL
|
24
|
+
@opts[:consul_addr] = 'http://localhost:8500'
|
25
|
+
|
26
|
+
|
27
|
+
OptionParser.new do |o|
|
28
|
+
o.banner = 'Usage: waiter.rb [options] -- exec'
|
29
|
+
|
30
|
+
o.on('--tcp host:port', 'Wait for tcp accepts on host:port') do |addr|
|
31
|
+
host, port = addr.split(':')
|
32
|
+
@opts[:tcp] = addr.strip
|
33
|
+
@opts[:host] = host.strip
|
34
|
+
@opts[:port] = port.strip
|
35
|
+
end
|
36
|
+
|
37
|
+
o.on('--db dbname', 'Wait for PG database exists. Using --tcp to conenct PG') do |db|
|
38
|
+
@opts[:db] = db.strip
|
39
|
+
end
|
40
|
+
|
41
|
+
o.on('--tb tablename', 'Wait for PG table exists. Using --tcp to conenct PG') do |tb|
|
42
|
+
@opts[:tb] = tb.strip
|
43
|
+
end
|
44
|
+
|
45
|
+
o.on('-f', '--file filename', 'Wait for file exists.') do |file|
|
46
|
+
@opts[:file] = file.strip
|
47
|
+
end
|
48
|
+
|
49
|
+
o.on("--consul-addr addr=#{@opts[:consul_addr]}", 'HTTP addres to connect to consul') do |addr|
|
50
|
+
@opts[:consul_addr] = addr.strip
|
51
|
+
end
|
52
|
+
|
53
|
+
o.on('--consul', 'Wait for local consul agent to be ready') do
|
54
|
+
@opts[:consul] = true
|
55
|
+
end
|
56
|
+
|
57
|
+
o.on('--consul-service service', 'Wait for service appear in consul') do |service|
|
58
|
+
@opts[:consul_service] = service
|
59
|
+
end
|
60
|
+
|
61
|
+
o.on('--user user', 'username') do |user|
|
62
|
+
@opts[:user] = user.strip
|
63
|
+
end
|
64
|
+
|
65
|
+
o.on('--pass pass', 'password') do |pass|
|
66
|
+
@opts[:pass] = pass.strip
|
67
|
+
end
|
68
|
+
|
69
|
+
o.on('-t', '--timeout secs=15', 'Total timeout') do |timeout|
|
70
|
+
@opts[:timeout] = timeout.to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
o.on('-i', '--interval secs=2', 'Interval between attempts') do |interval|
|
74
|
+
@opts[:interval] = interval.to_i
|
75
|
+
end
|
76
|
+
|
77
|
+
o.on('-q', '--quiet', 'Do not output any status messages') do
|
78
|
+
@opts[:quiet] = true
|
79
|
+
end
|
80
|
+
end.parse!
|
81
|
+
|
82
|
+
def log(message)
|
83
|
+
puts message unless @opts[:quiet]
|
84
|
+
end
|
85
|
+
|
86
|
+
@opts[:timeout] = @opts[:timeout].to_i
|
87
|
+
@opts[:interval] = @opts[:interval].to_i
|
88
|
+
|
89
|
+
if @opts[:db]
|
90
|
+
@pg = {}
|
91
|
+
@pg[:db] = "-d #{@opts[:db]}"
|
92
|
+
@pg[:user] = "-U #{@opts[:user]}" if @opts[:user]
|
93
|
+
@pg[:pass] = if @opts[:pass] && !@opts[:pass].empty?
|
94
|
+
"-W #{@opts[:pass]}"
|
95
|
+
else
|
96
|
+
'-w'
|
97
|
+
end
|
98
|
+
|
99
|
+
@pg[:host] = "-h #{@opts[:host]}" if @opts[:host]
|
100
|
+
@pg[:port] = "-p #{@opts[:port]}" if @opts[:port]
|
101
|
+
|
102
|
+
@pg[:tb] = @opts[:tb] if @opts[:tb]
|
103
|
+
end
|
104
|
+
|
105
|
+
def wait_for(timeout)
|
106
|
+
starttime = Time.now
|
107
|
+
loop do
|
108
|
+
success = yield
|
109
|
+
|
110
|
+
return success if success
|
111
|
+
|
112
|
+
return false if (Time.now - starttime) > timeout
|
113
|
+
sleep @opts[:interval]
|
114
|
+
end
|
115
|
+
|
116
|
+
false
|
117
|
+
end
|
118
|
+
|
119
|
+
def complete!(success)
|
120
|
+
if success
|
121
|
+
if @opts[:exec]
|
122
|
+
exec @opts[:exec]
|
123
|
+
else
|
124
|
+
exit 0
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
STDERR.puts 'Operation timed out'
|
129
|
+
exit 1
|
130
|
+
end
|
131
|
+
|
132
|
+
def wait_for_consul
|
133
|
+
log('Waiting for consul...')
|
134
|
+
ret = wait_for @opts[:timeout] do
|
135
|
+
cmd = "consul operator raft list-peers -http-addr=#{@opts[:consul_addr]} > /dev/null 2>&1"
|
136
|
+
system(cmd)
|
137
|
+
$CHILD_STATUS.success?
|
138
|
+
end
|
139
|
+
|
140
|
+
yield(ret)
|
141
|
+
end
|
142
|
+
|
143
|
+
def wait_for_consul_service(service)
|
144
|
+
log("Waiting for consul service #{service}...")
|
145
|
+
ret = wait_for @opts[:timeout] do
|
146
|
+
cmd = "curl -s #{@opts[:consul_addr]}/v1/health/service/#{service}?passing | wc -c"
|
147
|
+
bytes = `#{cmd}`.to_i
|
148
|
+
bytes > 10
|
149
|
+
end
|
150
|
+
|
151
|
+
yield(ret)
|
152
|
+
end
|
153
|
+
|
154
|
+
def wait_for_tcp
|
155
|
+
log("Waiting for TCP: #{@opts[:host]}:#{@opts[:port]}...")
|
156
|
+
ret = wait_for @opts[:timeout] do
|
157
|
+
cmd = "nc -z #{@opts[:host]} #{@opts[:port]} > /dev/null 2>&1"
|
158
|
+
system(cmd)
|
159
|
+
$CHILD_STATUS.success?
|
160
|
+
end
|
161
|
+
|
162
|
+
yield(ret)
|
163
|
+
end
|
164
|
+
|
165
|
+
def wait_for_db
|
166
|
+
log("Waiting for DB: pg://#{@opts[:user]}:#{@opts[:pass]}@#{@opts[:host]}:#{@opts[:port]}/#{@opts[:db]}...")
|
167
|
+
ret = wait_for @opts[:timeout] do
|
168
|
+
cmd = "psql -lqt #{@pg[:user]} #{@pg[:pass]} #{@pg[:host]} #{@pg[:port]} #{@pg[:db]} 2>/dev/null | cut -d \\| -f 1 | grep -qw #{@opts[:db]} > /dev/null 2>&1"
|
169
|
+
system(cmd)
|
170
|
+
$CHILD_STATUS.success?
|
171
|
+
end
|
172
|
+
|
173
|
+
yield(ret)
|
174
|
+
end
|
175
|
+
|
176
|
+
def wait_for_tb
|
177
|
+
log("Waiting for TABLE: pg://#{@opts[:user]}:#{@opts[:pass]}@#{@opts[:host]}:#{@opts[:port]}/#{@opts[:db]}##{@opts[:tb]}...")
|
178
|
+
ret = wait_for @opts[:timeout] do
|
179
|
+
cmd = "echo \"\\dt\" | psql -qt #{@pg[:user]} #{@pg[:pass]} #{@pg[:host]} #{@pg[:port]} #{@pg[:db]} 2>/dev/null | cut -d \\| -f 2 | grep -qw #{@pg[:tb]} > /dev/null 2>&1"
|
180
|
+
system(cmd)
|
181
|
+
$CHILD_STATUS.success?
|
182
|
+
end
|
183
|
+
|
184
|
+
yield(ret)
|
185
|
+
end
|
186
|
+
|
187
|
+
def wait_for_file(file = @opts[:file], timeout = @opts[:timeout])
|
188
|
+
log("Waiting for FILE: #{file}")
|
189
|
+
ret = wait_for timeout do
|
190
|
+
File.exist? file
|
191
|
+
end
|
192
|
+
yield(ret)
|
193
|
+
end
|
194
|
+
|
195
|
+
if @opts[:tb]
|
196
|
+
wait_for_tcp do |success|
|
197
|
+
if success
|
198
|
+
wait_for_db do |success|
|
199
|
+
if success
|
200
|
+
wait_for_tb do |success|
|
201
|
+
complete!(success)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
complete!(false)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if @opts[:db]
|
212
|
+
wait_for_tcp do |success|
|
213
|
+
if success
|
214
|
+
wait_for_db do |success|
|
215
|
+
complete!(success)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
complete!(false)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
if @opts[:consul]
|
225
|
+
wait_for_consul do |success|
|
226
|
+
complete!(success)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
if @opts[:consul_service]
|
231
|
+
wait_for_consul_service(@opts[:consul_service]) do |success|
|
232
|
+
complete!(success)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
if @opts[:tcp]
|
237
|
+
wait_for_tcp do |success|
|
238
|
+
complete!(success)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
if @opts[:file]
|
243
|
+
wait_for_file(@opts[:file], @opts[:timeout]) do |success|
|
244
|
+
complete!(success)
|
245
|
+
end
|
246
|
+
end
|
data/docker/Dockerfile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
FROM ruby:2.5.0-alpine
|
2
|
+
MAINTAINER Firmhouse "kinnalru@gmail.com"
|
3
|
+
|
4
|
+
WORKDIR /home/app
|
5
|
+
|
6
|
+
RUN mkdir -p ~/.ssh && echo -e "Host * \
|
7
|
+
\n StrictHostKeyChecking no \
|
8
|
+
\n UserKnownHostsFile=/dev/null\n" >> ~/.ssh/config
|
9
|
+
|
10
|
+
RUN set -ex \
|
11
|
+
&& apk add --no-cache \
|
12
|
+
bash \
|
13
|
+
libstdc++ \
|
14
|
+
libxml2 \
|
15
|
+
ruby-libs \
|
16
|
+
wget \
|
17
|
+
curl \
|
18
|
+
libcurl \
|
19
|
+
ruby \
|
20
|
+
git \
|
21
|
+
tzdata
|
22
|
+
|
23
|
+
RUN set -ex \
|
24
|
+
&& curl -sSLo /tmp/consul.zip https://releases.hashicorp.com/consul/1.2.1/consul_1.2.1_linux_amd64.zip \
|
25
|
+
&& unzip -d /bin /tmp/consul.zip \
|
26
|
+
&& rm -rf /tmp/consul.zip \
|
27
|
+
&& addgroup consul \
|
28
|
+
&& adduser -D -g "" -s /bin/sh -G consul consul \
|
29
|
+
&& mkdir -p /tmp/consul \
|
30
|
+
&& chown -R consul:consul /tmp/consul
|
31
|
+
|
32
|
+
RUN set -ex \
|
33
|
+
&& curl -so envconsul.tgz https://releases.hashicorp.com/envconsul/0.7.3/envconsul_0.7.3_linux_amd64.tgz \
|
34
|
+
&& tar -xvzf envconsul.tgz \
|
35
|
+
&& rm -rf envconsul.tgz \
|
36
|
+
&& mv envconsul /bin/envconsul \
|
37
|
+
&& chmod +x /bin/envconsul
|
38
|
+
|
39
|
+
ADD Gemfile Gemfile.lock docker_toolkit.gemspec /home/app/
|
40
|
+
ADD lib/ /home/app/lib
|
41
|
+
|
42
|
+
RUN set -ex \
|
43
|
+
&& bundle install \
|
44
|
+
&& cd / \
|
45
|
+
&& rm -rf /usr/src/ruby
|
46
|
+
|
47
|
+
ADD . /home/app
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
SHELL ["/bin/sh", "-c", "-l"]
|
52
|
+
|
53
|
+
|
data/docker/down.sh
ADDED
data/docker/run_tests.sh
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
SELF=$(readlink -f $0)
|
3
|
+
export ENV_ROOT=$(dirname "${SELF}")
|
4
|
+
|
5
|
+
trap '$ENV_ROOT/down.sh' EXIT
|
6
|
+
|
7
|
+
cd $ENV_ROOT
|
8
|
+
|
9
|
+
docker-compose up -d --force-recreate --build || exit 1
|
10
|
+
docker-compose exec -T tests bundle exec rspec $@
|
11
|
+
|
12
|
+
code=$?
|
13
|
+
if [ $code -ne 0 ]; then
|
14
|
+
docker-compose logs
|
15
|
+
fi
|
16
|
+
|
17
|
+
exit $code
|
@@ -0,0 +1,33 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'docker_toolkit/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'docker_toolkit'
|
7
|
+
spec.version = DockerToolkit::VERSION
|
8
|
+
spec.authors = ['Godko Ivan', 'Samoilenko Yuri']
|
9
|
+
spec.email = ['igodko@rnds.pro', 'kinnalru@gmail.com']
|
10
|
+
spec.homepage = 'https://github.com/RnD-Soft/docker_toolkit'
|
11
|
+
spec.required_ruby_version = '>= 2.1.0'
|
12
|
+
|
13
|
+
spec.summary = 'Helper scripts for work with docker and consul'
|
14
|
+
spec.description = 'Advanced docker and consul control scripts'
|
15
|
+
|
16
|
+
spec.bindir = 'bin'
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
if File.exist?(File.join(__dir__, '/', '.git'))
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f) }
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
spec.add_dependency 'diplomat'
|
29
|
+
spec.add_dependency 'json'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: docker_toolkit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Godko Ivan
|
8
|
+
- Samoilenko Yuri
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-08-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: diplomat
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: json
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.14'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.14'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '10.0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '10.0'
|
70
|
+
description: Advanced docker and consul control scripts
|
71
|
+
email:
|
72
|
+
- igodko@rnds.pro
|
73
|
+
- kinnalru@gmail.com
|
74
|
+
executables:
|
75
|
+
- consul.rb
|
76
|
+
- merger.rb
|
77
|
+
- waiter.rb
|
78
|
+
extensions: []
|
79
|
+
extra_rdoc_files: []
|
80
|
+
files:
|
81
|
+
- ".dockerignore"
|
82
|
+
- ".gitignore"
|
83
|
+
- ".rspec"
|
84
|
+
- ".rubocop.yml"
|
85
|
+
- ".travis.yml"
|
86
|
+
- Gemfile
|
87
|
+
- Gemfile.lock
|
88
|
+
- README.md
|
89
|
+
- bin/consul.rb
|
90
|
+
- bin/merger.rb
|
91
|
+
- bin/waiter.rb
|
92
|
+
- docker/Dockerfile
|
93
|
+
- docker/docker-compose.yml
|
94
|
+
- docker/down.sh
|
95
|
+
- docker/run_tests.sh
|
96
|
+
- docker_toolkit.gemspec
|
97
|
+
- lib/docker_toolkit.rb
|
98
|
+
- lib/docker_toolkit/version.rb
|
99
|
+
homepage: https://github.com/RnD-Soft/docker_toolkit
|
100
|
+
licenses: []
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.1.0
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.7.3
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Helper scripts for work with docker and consul
|
122
|
+
test_files: []
|