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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c565af80fd33cee75c95b7ada9c8c7bdb42f0bf0
4
- data.tar.gz: 288e91970419af210886a50ab0d78cbd95a3f203
3
+ metadata.gz: 78dfed44ff1431c3e5216b9d354a1dd208b5f479
4
+ data.tar.gz: 8e5b2f9ec42d2a826e3814cf6c122a33905b5273
5
5
  SHA512:
6
- metadata.gz: 4bb58cfef7d4fbf1e344b61c32117323e5730dd879938bf1d10033abb408e8a3e029a833235474d55a0166b61ac1750ff073102246568080ee9437e17b24c45c
7
- data.tar.gz: 216df1896cb8ac1fbdef8994c87f50a863e710ef49ef660144ac30eb0f980d2822279332c8855593706b3fdf134abe56e1da681cc542303e7a068bc559c3194d
6
+ metadata.gz: cc5c27596574efc2690de74f50d9c2a4bfe28f9c0badbc2f64bc112c4ffb1026d68ea625add78bf4109e30460cee2cfda3382e2d8ab5fd6878fd369b0f8acebe
7
+ data.tar.gz: 8432d2afe6b3a3a91c0e1127963270617dde46b7a4983648ea9559567b4a129c0ddc5fc1ccfdcb857697fca25797c77516871461295bba13152acf2410ebd08e
@@ -1,5 +1,6 @@
1
- require 'docker_rails_proxy/extends/colorization'
2
- require 'docker_rails_proxy/extends/string_support'
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
- '#{APP_PATH}'
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|
@@ -62,7 +62,7 @@ module DockerRailsProxy
62
62
  def rsync_app
63
63
  loop do
64
64
  break if sync(source: APP_PATH)
65
- puts "waiting for rsync-volume service on #{rsync_host}"
65
+ puts "waiting for rsync-volume service on #{_rsync_host}"
66
66
  sleep 2
67
67
  end
68
68
  end
@@ -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
@@ -69,6 +69,8 @@ module DockerRailsProxy
69
69
  --exclude 'tmp/cache' \
70
70
  --exclude 'tmp/letter_opener' \
71
71
  --exclude 'tmp/pids' \
72
+ --exclude 'tmp/sockets' \
73
+ --exclude 'tmp/sessions' \
72
74
  --exclude 'log' \
73
75
  --exclude '.git*' \
74
76
  --force \
@@ -0,0 +1,9 @@
1
+ class Fixnum
2
+ def present?
3
+ true
4
+ end
5
+
6
+ def blank?
7
+ false
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ class NilClass
2
+ def present?
3
+ false
4
+ end
5
+
6
+ def empty?
7
+ true
8
+ end
9
+
10
+ def blank?
11
+ true
12
+ end
13
+ end
@@ -1,12 +1,13 @@
1
1
  class String
2
- def parameterize(separator = '-'.freeze)
3
- downcase.gsub(/\W/, separator).gsub('_'.freeze, separator)
2
+ def blank?
3
+ nil? or empty?
4
4
  end
5
5
 
6
6
  def classify
7
- gsub(/\W/, ''.freeze)
8
- .gsub('_'.freeze, ''.freeze)
9
- .sub(/^[a-z\d]*/, &:capitalize)
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
@@ -1,3 +1,3 @@
1
1
  module DockerRailsProxy
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  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.1
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-09-30 00:00:00.000000000 Z
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