docker_rails_proxy 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/docker_rails_proxy.rb +33 -2
- data/lib/docker_rails_proxy/commands/build.rb +16 -2
- data/lib/docker_rails_proxy/commands/compose/up.rb +1 -1
- data/lib/docker_rails_proxy/commands/stack.rb +98 -0
- data/lib/docker_rails_proxy/commands/stack/create.rb +181 -0
- data/lib/docker_rails_proxy/commands/stack/destroy.rb +48 -0
- data/lib/docker_rails_proxy/concerns/rsync.rb +2 -0
- data/lib/docker_rails_proxy/extends/fixnum_support.rb +9 -0
- data/lib/docker_rails_proxy/extends/nil_class_support.rb +13 -0
- data/lib/docker_rails_proxy/extends/string_support.rb +18 -5
- data/lib/docker_rails_proxy/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78dfed44ff1431c3e5216b9d354a1dd208b5f479
|
4
|
+
data.tar.gz: 8e5b2f9ec42d2a826e3814cf6c122a33905b5273
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc5c27596574efc2690de74f50d9c2a4bfe28f9c0badbc2f64bc112c4ffb1026d68ea625add78bf4109e30460cee2cfda3382e2d8ab5fd6878fd369b0f8acebe
|
7
|
+
data.tar.gz: 8432d2afe6b3a3a91c0e1127963270617dde46b7a4983648ea9559567b4a129c0ddc5fc1ccfdcb857697fca25797c77516871461295bba13152acf2410ebd08e
|
data/lib/docker_rails_proxy.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
Dir[File.expand_path('../docker_rails_proxy/extends/*.rb', __FILE__)].map do |f|
|
2
|
+
require f
|
3
|
+
end
|
3
4
|
|
4
5
|
module DockerRailsProxy
|
5
6
|
COMMANDS = Dir[File.expand_path('../docker_rails_proxy/commands/*.rb', __FILE__)].map do |f|
|
@@ -33,6 +34,16 @@ module DockerRailsProxy
|
|
33
34
|
File.join(APP_PATH, block_given? ? yield : path)
|
34
35
|
end
|
35
36
|
|
37
|
+
def command
|
38
|
+
name.sub('DockerRailsProxy::', ''.freeze).parameterize.sub('--', ' '.freeze)
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute(options)
|
42
|
+
system <<-EOS
|
43
|
+
#{build_path("bin/#{APP_NAME}")} #{command} #{options}
|
44
|
+
EOS
|
45
|
+
end
|
46
|
+
|
36
47
|
def call(options)
|
37
48
|
klass = _run_build_callbacks params: options
|
38
49
|
|
@@ -62,6 +73,26 @@ module DockerRailsProxy
|
|
62
73
|
def build_path(*args, &block)
|
63
74
|
self.class.build_path(*args, &block)
|
64
75
|
end
|
76
|
+
|
77
|
+
def print_options(values, message)
|
78
|
+
puts message
|
79
|
+
values.each_with_index { |v, i| puts "#{i}) #{v}" }
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_option(values, default = nil)
|
83
|
+
flush_stdin
|
84
|
+
print ": "
|
85
|
+
option = $stdin.gets.chomp
|
86
|
+
|
87
|
+
return default if option.blank?
|
88
|
+
option =~ /^\d+$/ ? values[option.to_i] : nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def flush_stdin
|
92
|
+
loop do
|
93
|
+
Timeout::timeout(0.1) { $stdin.gets.chomp } rescue break
|
94
|
+
end
|
95
|
+
end
|
65
96
|
end
|
66
97
|
|
67
98
|
class AwsCli < Base
|
@@ -5,7 +5,7 @@ module DockerRailsProxy
|
|
5
5
|
attr_accessor :options
|
6
6
|
|
7
7
|
after_initialize { self.options = {} }
|
8
|
-
after_initialize :parse_options
|
8
|
+
after_initialize :parse_options!, :set_defaults
|
9
9
|
|
10
10
|
validates do
|
11
11
|
if options[:dockerfile].nil?
|
@@ -17,6 +17,12 @@ module DockerRailsProxy
|
|
17
17
|
|
18
18
|
validates { '--tag is required' if options[:tag].nil? }
|
19
19
|
|
20
|
+
validates do
|
21
|
+
unless File.directory?(options[:context])
|
22
|
+
"#{options[:context]} folder does not exist."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
20
26
|
validates do
|
21
27
|
options[:build_args] ||= {}
|
22
28
|
|
@@ -37,7 +43,7 @@ module DockerRailsProxy
|
|
37
43
|
-f '#{options[:dockerfile]}' \
|
38
44
|
-t '#{options[:tag]}' \
|
39
45
|
#{build_args} \
|
40
|
-
'#{
|
46
|
+
'#{options[:context]}'
|
41
47
|
EOS
|
42
48
|
end
|
43
49
|
|
@@ -49,6 +55,10 @@ module DockerRailsProxy
|
|
49
55
|
end.join(' ')
|
50
56
|
end
|
51
57
|
|
58
|
+
def set_defaults
|
59
|
+
options[:context] ||= File.dirname(options[:dockerfile])
|
60
|
+
end
|
61
|
+
|
52
62
|
def parse_options!
|
53
63
|
opt_parser.parse!(arguments)
|
54
64
|
end
|
@@ -61,6 +71,10 @@ module DockerRailsProxy
|
|
61
71
|
options[:dockerfile] = dockerfile
|
62
72
|
end
|
63
73
|
|
74
|
+
opts.on('--context [CONTEXT]', 'Docker build context') do |context|
|
75
|
+
options[:context] = context
|
76
|
+
end
|
77
|
+
|
64
78
|
opts.on('--tag TAG', 'Docker Image Tag') { |tag| options[:tag] = tag }
|
65
79
|
|
66
80
|
opts.on('--build-args A=val,B=val...', Array, 'Docker build-args') do |o|
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module DockerRailsProxy
|
4
|
+
class Stack < AwsCli
|
5
|
+
autoload :Create, 'docker_rails_proxy/commands/stack/create'
|
6
|
+
autoload :Destroy, 'docker_rails_proxy/commands/stack/destroy'
|
7
|
+
|
8
|
+
RUNNING_STATUSES = %w[CREATE_COMPLETE UPDATE_COMPLETE].freeze
|
9
|
+
|
10
|
+
attr_accessor :options
|
11
|
+
|
12
|
+
before_initialize do
|
13
|
+
'jq is required, `brew install jq`' unless system 'type jq &> /dev/null'
|
14
|
+
end
|
15
|
+
|
16
|
+
after_initialize { self.options = {} }
|
17
|
+
after_initialize { opt_parser.parse!(arguments) }
|
18
|
+
after_initialize { options[:profile] ||= APP_NAME }
|
19
|
+
|
20
|
+
validates { '--profile is required.' if options[:profile].blank? }
|
21
|
+
|
22
|
+
builds -> (params:) do
|
23
|
+
case params[:arguments].shift
|
24
|
+
when 'create' then Create
|
25
|
+
when 'destroy' then Destroy
|
26
|
+
when 'deploy'
|
27
|
+
klass_name = %W[
|
28
|
+
DockerRailsProxy
|
29
|
+
Stack
|
30
|
+
#{params[:arguments].first.classify}Deploy
|
31
|
+
].join('::')
|
32
|
+
|
33
|
+
begin
|
34
|
+
klass_name.constantize
|
35
|
+
rescue
|
36
|
+
$stderr.puts "#{klass_name} class does not exit"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
else
|
40
|
+
puts "Usage: bin/#{APP_NAME} stack <create|destroy|deploy> [options]"
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def stack_exist?(stack_name)
|
48
|
+
!stack_status(stack_name).blank?
|
49
|
+
end
|
50
|
+
|
51
|
+
def wait_for_stack(stack_name)
|
52
|
+
loop do
|
53
|
+
case status = stack_status(stack_name)
|
54
|
+
when 'CREATE_IN_PROGRESS', 'UPDATE_IN_PROGRESS', 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS'
|
55
|
+
puts "#{stack_name} still processing: #{status}"
|
56
|
+
|
57
|
+
when *RUNNING_STATUSES
|
58
|
+
puts "#{stack_name} stack stabilized: #{status}"
|
59
|
+
break
|
60
|
+
|
61
|
+
else
|
62
|
+
$stderr.puts %{
|
63
|
+
There is a problem with the #{stack_name} stack: #{status}
|
64
|
+
}
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
sleep 10
|
69
|
+
end if stack_exist?(stack_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def stack_status(stack_name)
|
73
|
+
%x(aws cloudformation describe-stacks \
|
74
|
+
--profile '#{options[:profile]}' \
|
75
|
+
| jq '.Stacks[] | select(.StackName == "#{stack_name}") | .StackStatus' \
|
76
|
+
| xargs
|
77
|
+
).strip
|
78
|
+
end
|
79
|
+
|
80
|
+
def opt_parser
|
81
|
+
@opt_parser ||= OptionParser.new do |opts|
|
82
|
+
opts.banner = "Usage: bin/#{APP_NAME} stack #{self.class.name.demodulize.parameterize} [options]"
|
83
|
+
|
84
|
+
opts.on(
|
85
|
+
'--profile [PROFILE]',
|
86
|
+
"Aws profile (Default: #{APP_NAME})"
|
87
|
+
) { |profile| options[:profile] = profile }
|
88
|
+
|
89
|
+
yield opts if block_given?
|
90
|
+
|
91
|
+
opts.on('-h', '--help', 'Display this screen') do
|
92
|
+
puts opts
|
93
|
+
exit
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'timeout'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module DockerRailsProxy
|
6
|
+
class Stack < AwsCli
|
7
|
+
class Create < self
|
8
|
+
YML_EXTENSIONS = %w(.yml .yaml).freeze
|
9
|
+
|
10
|
+
attr_accessor :data, :parameters, :outputs
|
11
|
+
|
12
|
+
after_initialize { self.parameters, self.outputs = {}, {} }
|
13
|
+
after_initialize :set_defaults
|
14
|
+
|
15
|
+
validates { '--stack-name is required.' if options[:stack_name].blank? }
|
16
|
+
validates { '--ymlfile is required.' if options[:ymlfile].blank? }
|
17
|
+
|
18
|
+
validates do
|
19
|
+
unless File.exist? options[:ymlfile]
|
20
|
+
"#{options[:ymlfile]} file does not exit"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
validates do
|
25
|
+
if YML_EXTENSIONS.include? File.extname(options[:ymlfile])
|
26
|
+
self.data = YAML::load_file(options[:ymlfile])
|
27
|
+
options[:jsonfile] = options[:ymlfile].sub(/\..+/, '.json')
|
28
|
+
File.write(options[:jsonfile], data.to_json)
|
29
|
+
else
|
30
|
+
"#{options[:ymlfile]} is not a yml file"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
validates do
|
35
|
+
if options[:wait_for_stack]
|
36
|
+
wait_for_stack(options[:stack_name])
|
37
|
+
'' # Just exit, wait_for_stack will print messages
|
38
|
+
else
|
39
|
+
"Stack: #{options[:stack_name]} already exists"
|
40
|
+
end if stack_exist?(options[:stack_name])
|
41
|
+
end
|
42
|
+
|
43
|
+
validates do
|
44
|
+
unless system <<-EOS
|
45
|
+
aws cloudformation validate-template \
|
46
|
+
--template-body 'file://#{options[:jsonfile]}' \
|
47
|
+
--profile '#{options[:profile]}' \
|
48
|
+
> /dev/null
|
49
|
+
EOS
|
50
|
+
|
51
|
+
%{
|
52
|
+
Invalid template. See above errors
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
before_process { set_outputs unless options[:import_outputs_from].empty? }
|
58
|
+
before_process { set_parameters }
|
59
|
+
|
60
|
+
after_process { File.delete(options[:jsonfile]) if File.exist?(options[:jsonfile]) }
|
61
|
+
after_process { wait_for_stack(options[:stack_name]) if options[:wait_for_stack] }
|
62
|
+
|
63
|
+
def process
|
64
|
+
system <<-EOS
|
65
|
+
aws cloudformation create-stack \
|
66
|
+
--stack-name '#{options[:stack_name]}' \
|
67
|
+
--parameters #{parameters.join(' ')} \
|
68
|
+
--template-body 'file://#{options[:jsonfile]}' \
|
69
|
+
--capabilities 'CAPABILITY_IAM' \
|
70
|
+
--profile '#{options[:profile]}'
|
71
|
+
EOS
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def set_outputs
|
77
|
+
jq_command = <<-EOS
|
78
|
+
.Stacks[]
|
79
|
+
| select(
|
80
|
+
.StackName as $name
|
81
|
+
| "#{options[:import_outputs_from].join(' ')}"
|
82
|
+
| split(" ")
|
83
|
+
| map(. == $name)
|
84
|
+
| index(true) >= 0
|
85
|
+
)
|
86
|
+
| .Outputs[]
|
87
|
+
| [ .OutputKey, .OutputValue ]
|
88
|
+
| join("=")
|
89
|
+
EOS
|
90
|
+
|
91
|
+
outputs_data = %x(
|
92
|
+
aws cloudformation describe-stacks --profile '#{options[:profile]}' \
|
93
|
+
| jq '#{jq_command}' | xargs
|
94
|
+
).strip.split(' ')
|
95
|
+
|
96
|
+
outputs_data.each do |string|
|
97
|
+
key, value = string.split('=')
|
98
|
+
self.outputs[key] = value
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_parameters
|
103
|
+
puts '-' * 100
|
104
|
+
puts "- #{options[:stack_name]} stack parameters"
|
105
|
+
|
106
|
+
(data['Parameters'] || {}).each do |key, attrs|
|
107
|
+
parameters[key] = options[:parameters][key]
|
108
|
+
parameters[key] ||= outputs[key]
|
109
|
+
|
110
|
+
next if parameters[key].present?
|
111
|
+
|
112
|
+
puts '-' * 100
|
113
|
+
|
114
|
+
while parameters[key].to_s.blank? do
|
115
|
+
value = nil
|
116
|
+
|
117
|
+
case attrs['Type']
|
118
|
+
when 'AWS::EC2::KeyPair::KeyName'
|
119
|
+
key_pairs ||= %x(
|
120
|
+
aws ec2 describe-key-pairs --profile '#{options[:profile]}' \
|
121
|
+
| jq '.KeyPairs[] | .KeyName' | xargs
|
122
|
+
).strip.split(' ')
|
123
|
+
|
124
|
+
print_options(key_pairs, "Choose an option for #{key} and press [ENTER]")
|
125
|
+
parameters[key] = get_option(key_pairs, value)
|
126
|
+
|
127
|
+
else
|
128
|
+
value ||= attrs['Default']
|
129
|
+
|
130
|
+
allowed_values = Array(attrs['AllowedValues'])
|
131
|
+
|
132
|
+
if allowed_values.empty?
|
133
|
+
print "Enter #{key} value and press [ENTER] (Default: #{value}): "
|
134
|
+
flush_stdin
|
135
|
+
|
136
|
+
parameters[key] = $stdin.gets.chomp || value
|
137
|
+
parameters[key] = value if parameters[key].blank?
|
138
|
+
else
|
139
|
+
print_options(allowed_values, "Choose an option for #{key} and press [ENTER] (Default: #{value})")
|
140
|
+
parameters[key] = get_option(allowed_values, value)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
self.parameters = parameters.map do |key, value|
|
147
|
+
"ParameterKey=\"#{key}\",ParameterValue=\"#{value}\",UsePreviousValue=false"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def set_defaults
|
152
|
+
options[:parameters] ||= {}
|
153
|
+
options[:import_outputs_from] ||= []
|
154
|
+
end
|
155
|
+
|
156
|
+
def opt_parser
|
157
|
+
super do |opts|
|
158
|
+
opts.on('--stack-name STACK_NAME', 'Stack Name') do |stack_name|
|
159
|
+
options[:stack_name] = stack_name
|
160
|
+
end
|
161
|
+
|
162
|
+
opts.on('--ymlfile YMLFILE', 'Stack YML file') do |ymlfile|
|
163
|
+
options[:ymlfile] = build_path(ymlfile)
|
164
|
+
end
|
165
|
+
|
166
|
+
opts.on('--parameters A=val,B=val...', Array, 'CF parameters') do |o|
|
167
|
+
options[:parameters] = Hash[o.map { |s| s.split('=', 2) }]
|
168
|
+
end
|
169
|
+
|
170
|
+
opts.on('--import-outputs-from a,b...', Array, 'CF stack names') do |list|
|
171
|
+
options[:import_outputs_from] = list
|
172
|
+
end
|
173
|
+
|
174
|
+
opts.on('--wait-for-stack', 'Wait for the stack to be created') do |v|
|
175
|
+
options[:wait_for_stack] = v
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module DockerRailsProxy
|
2
|
+
class Stack < AwsCli
|
3
|
+
class Destroy < self
|
4
|
+
attr_accessor :stacks
|
5
|
+
|
6
|
+
before_process do
|
7
|
+
jq_command = <<-EOS
|
8
|
+
.Stacks
|
9
|
+
| map(
|
10
|
+
select(
|
11
|
+
.StackStatus | inside("CREATE_COMPLETE UPDATE_COMPLETE")
|
12
|
+
)
|
13
|
+
| .StackName
|
14
|
+
)
|
15
|
+
| sort[]
|
16
|
+
EOS
|
17
|
+
|
18
|
+
self.stacks = %x(
|
19
|
+
aws cloudformation describe-stacks --profile '#{options[:profile]}' \
|
20
|
+
| jq '#{jq_command}' | xargs
|
21
|
+
).strip.split(' ')
|
22
|
+
|
23
|
+
if stacks.empty?
|
24
|
+
$stderr.puts 'There are no stacks running'
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def process
|
30
|
+
begin
|
31
|
+
print_options(stacks, 'Choose the stack number and press [ENTER]')
|
32
|
+
stack_name = get_option(stacks)
|
33
|
+
end while stack_name.blank?
|
34
|
+
|
35
|
+
puts "You're about to destroy this stack: #{stack_name}, are you sure? [yes]:"
|
36
|
+
exit unless $stdin.gets.chomp == 'yes'
|
37
|
+
|
38
|
+
puts "Destroying #{stack_name} stack"
|
39
|
+
|
40
|
+
system <<-EOS
|
41
|
+
aws cloudformation delete-stack \
|
42
|
+
--stack-name '#{stack_name}' \
|
43
|
+
--profile '#{options[:profile]}'
|
44
|
+
EOS
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
class String
|
2
|
-
def
|
3
|
-
|
2
|
+
def blank?
|
3
|
+
nil? or empty?
|
4
4
|
end
|
5
5
|
|
6
6
|
def classify
|
7
|
-
gsub(
|
8
|
-
.gsub(
|
9
|
-
.
|
7
|
+
gsub('-'.freeze, '_'.freeze)
|
8
|
+
.gsub(/\W/, ''.freeze)
|
9
|
+
.split('_'.freeze)
|
10
|
+
.map{|s| s.sub(/^[a-z\d]*/, &:capitalize) }.join
|
10
11
|
end
|
11
12
|
|
12
13
|
def constantize
|
@@ -15,6 +16,18 @@ class String
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
def demodulize
|
20
|
+
split('::').last
|
21
|
+
end
|
22
|
+
|
23
|
+
def parameterize(separator = '-'.freeze)
|
24
|
+
downcase.gsub(/\W/, separator).gsub('_'.freeze, separator)
|
25
|
+
end
|
26
|
+
|
27
|
+
def present?
|
28
|
+
!empty?
|
29
|
+
end
|
30
|
+
|
18
31
|
def underscore
|
19
32
|
downcase.gsub('::'.freeze, '/'.freeze).gsub('-'.freeze, '_'.freeze)
|
20
33
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docker_rails_proxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jairo
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-11-04 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Configures docker-compose and provides rails command helpers
|
15
15
|
email:
|
@@ -36,10 +36,15 @@ files:
|
|
36
36
|
- lib/docker_rails_proxy/commands/rspec.rb
|
37
37
|
- lib/docker_rails_proxy/commands/spring.rb
|
38
38
|
- lib/docker_rails_proxy/commands/ssh.rb
|
39
|
+
- lib/docker_rails_proxy/commands/stack.rb
|
40
|
+
- lib/docker_rails_proxy/commands/stack/create.rb
|
41
|
+
- lib/docker_rails_proxy/commands/stack/destroy.rb
|
39
42
|
- lib/docker_rails_proxy/concerns/callbacks.rb
|
40
43
|
- lib/docker_rails_proxy/concerns/inheritable_attributes.rb
|
41
44
|
- lib/docker_rails_proxy/concerns/rsync.rb
|
42
45
|
- lib/docker_rails_proxy/extends/colorization.rb
|
46
|
+
- lib/docker_rails_proxy/extends/fixnum_support.rb
|
47
|
+
- lib/docker_rails_proxy/extends/nil_class_support.rb
|
43
48
|
- lib/docker_rails_proxy/extends/string_support.rb
|
44
49
|
- lib/docker_rails_proxy/version.rb
|
45
50
|
homepage: https://github.com/jairovm/docker_rails_proxy
|