docker_rails_proxy 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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