orchestration 0.5.11 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -30
  3. data/config/locales/en.yml +11 -15
  4. data/lib/orchestration.rb +4 -0
  5. data/lib/orchestration/docker_compose/app_service.rb +8 -1
  6. data/lib/orchestration/docker_compose/configuration.rb +1 -1
  7. data/lib/orchestration/docker_compose/database_service.rb +1 -1
  8. data/lib/orchestration/docker_compose/install_generator.rb +4 -4
  9. data/lib/orchestration/docker_compose/rabbitmq_service.rb +4 -2
  10. data/lib/orchestration/environment.rb +1 -1
  11. data/lib/orchestration/errors.rb +2 -0
  12. data/lib/orchestration/install_generator.rb +2 -29
  13. data/lib/orchestration/make.rb +4 -0
  14. data/lib/orchestration/make/orchestration.mk +503 -0
  15. data/lib/orchestration/service_check.rb +24 -38
  16. data/lib/orchestration/services/database/adapters.rb +1 -0
  17. data/lib/orchestration/services/database/adapters/adapter_base.rb +21 -0
  18. data/lib/orchestration/services/database/adapters/mysql2.rb +4 -2
  19. data/lib/orchestration/services/database/adapters/postgresql.rb +2 -0
  20. data/lib/orchestration/services/database/adapters/sqlite3.rb +2 -0
  21. data/lib/orchestration/services/database/configuration.rb +17 -19
  22. data/lib/orchestration/services/mixins/configuration_base.rb +1 -1
  23. data/lib/orchestration/services/rabbitmq.rb +1 -0
  24. data/lib/orchestration/templates/Dockerfile.erb +5 -7
  25. data/lib/orchestration/templates/application.mk.erb +23 -7
  26. data/lib/orchestration/templates/rabbitmq.yml.erb +5 -2
  27. data/lib/orchestration/terminal.rb +4 -3
  28. data/lib/orchestration/version.rb +1 -1
  29. data/lib/tasks/orchestration.rake +23 -6
  30. data/orchestration.gemspec +6 -3
  31. metadata +64 -21
  32. data/lib/orchestration/templates/makefile_macros.mk.erb +0 -112
  33. data/lib/orchestration/templates/orchestration.mk.erb +0 -393
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Orchestration
4
4
  class ServiceCheck
5
- ATTEMPT_LIMIT = ENV.fetch('ORCHESTRATION_RETRY_LIMIT', '10').to_i
6
- RETRY_INTERVAL = ENV.fetch('ORCHESTRATION_RETRY_INTERVAL', '6').to_i
5
+ ATTEMPT_LIMIT = ENV.fetch('ORCHESTRATION_RETRY_LIMIT', '15').to_i
6
+ RETRY_INTERVAL = ENV.fetch('ORCHESTRATION_RETRY_INTERVAL', '15').to_i
7
7
 
8
8
  def initialize(service, terminal, options = {})
9
9
  @service = service
@@ -31,36 +31,36 @@ module Orchestration
31
31
  @service.connect
32
32
  true
33
33
  rescue *@service.connection_errors => e
34
- @attempts += 1
35
- sleep @retry_interval
34
+ wait_failure(e)
36
35
  retry unless @attempts == @attempt_limit
37
36
  echo_error(e)
38
37
  echo_failure
39
38
  false
40
39
  end
41
40
 
42
- def echo_start
43
- @terminal.write(@service_name.to_sym, '', :status)
41
+ def wait_failure(error)
42
+ @attempts += 1
43
+ @last_error = error
44
+ sleep @retry_interval
44
45
  end
45
46
 
46
- def echo_waiting
47
- @terminal.write(:waiting, service_waiting)
47
+ def last_error
48
+ return nil if @last_error.nil?
49
+
50
+ last_error_message
48
51
  end
49
52
 
50
- def service_waiting
51
- I18n.t(
52
- "orchestration.#{@service_name}.waiting",
53
- config: friendly_config,
54
- default: default_waiting
55
- )
53
+ def last_error_message
54
+ "(#{@last_error&.cause&.class&.name || @last_error&.class&.name})"
56
55
  end
57
56
 
58
- def default_waiting
59
- I18n.t(
60
- 'orchestration.custom_service.waiting',
61
- config: friendly_config,
62
- service: @service_name
63
- )
57
+ def echo_start
58
+ @terminal.write(@service_name.to_sym, '', :status)
59
+ @terminal.write(:config, friendly_config)
60
+ end
61
+
62
+ def echo_waiting
63
+ @terminal.write(:waiting, last_error)
64
64
  end
65
65
 
66
66
  def echo_ready
@@ -68,30 +68,16 @@ module Orchestration
68
68
  end
69
69
 
70
70
  def service_ready
71
- I18n.t(
72
- "orchestration.#{@service_name}.ready",
73
- config: friendly_config,
74
- default: default_ready
75
- )
76
- end
77
-
78
- def default_ready
79
- I18n.t(
80
- 'orchestration.custom_service.ready',
81
- config: friendly_config,
82
- service: @service_name
83
- )
71
+ I18n.t('orchestration.service.ready', service: @service_name)
84
72
  end
85
73
 
86
74
  def echo_failure
87
- @terminal.write(
88
- :failure,
89
- I18n.t('orchestration.attempt_limit', limit: @attempt_limit)
90
- )
75
+ @terminal.write(:failure, I18n.t('orchestration.attempt_limit', limit: @attempt_limit))
91
76
  end
92
77
 
93
78
  def echo_error(error)
94
- @terminal.write(:error, "[#{error.class.name}] #{error.message}")
79
+ cause = error.cause.nil? ? error : error.cause
80
+ @terminal.write(:error, "[#{cause.class.name}] #{cause.message}")
95
81
  end
96
82
 
97
83
  def friendly_config
@@ -9,6 +9,7 @@ module Orchestration
9
9
  end
10
10
  end
11
11
 
12
+ require 'orchestration/services/database/adapters/adapter_base'
12
13
  require 'orchestration/services/database/adapters/mysql2'
13
14
  require 'orchestration/services/database/adapters/postgresql'
14
15
  require 'orchestration/services/database/adapters/sqlite3'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orchestration
4
+ module Services
5
+ module Database
6
+ module Adapters
7
+ module AdapterBase
8
+ attr_reader :config
9
+
10
+ def initialize(config = nil)
11
+ @config = config
12
+ end
13
+
14
+ def console_command
15
+ I18n.t("orchestration.dbconsole.#{name}") % config.settings.transform_keys(&:to_sym)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -5,12 +5,14 @@ module Orchestration
5
5
  module Database
6
6
  module Adapters
7
7
  class Mysql2
8
+ include AdapterBase
9
+
8
10
  def name
9
11
  'mysql2'
10
12
  end
11
13
 
12
14
  def image
13
- return mysql5_7 if gem_version < Gem::Version.new('0.4')
15
+ return mysql57 if gem_version < Gem::Version.new('0.4')
14
16
 
15
17
  'library/mysql'
16
18
  end
@@ -43,7 +45,7 @@ module Orchestration
43
45
 
44
46
  private
45
47
 
46
- def mysql5_7
48
+ def mysql57
47
49
  'library/mysql:5.7'
48
50
  end
49
51
 
@@ -5,6 +5,8 @@ module Orchestration
5
5
  module Database
6
6
  module Adapters
7
7
  class Postgresql
8
+ include AdapterBase
9
+
8
10
  def name
9
11
  'postgresql'
10
12
  end
@@ -5,6 +5,8 @@ module Orchestration
5
5
  module Database
6
6
  module Adapters
7
7
  class Sqlite3
8
+ include AdapterBase
9
+
8
10
  def name
9
11
  'sqlite3'
10
12
  end
@@ -15,7 +15,7 @@ module Orchestration
15
15
  def friendly_config
16
16
  return "[#{adapter.name}]" if sqlite?
17
17
 
18
- "[#{adapter.name}] #{host}:#{port}"
18
+ "[#{adapter.name}] #{adapter.name}://#{username}:#{password}@#{host}:#{port}/#{database}"
19
19
  end
20
20
 
21
21
  def settings(healthcheck: false)
@@ -33,6 +33,10 @@ module Orchestration
33
33
  sqlite? || super
34
34
  end
35
35
 
36
+ def console_command
37
+ adapter.console_command
38
+ end
39
+
36
40
  def adapter
37
41
  url_adapter = url_config['adapter']
38
42
  file_adapter = file_config['adapter']
@@ -82,37 +86,31 @@ module Orchestration
82
86
  end
83
87
 
84
88
  def host
85
- url_config['host'] || file_config['host'] || super
89
+ chained_config('host') || super
86
90
  end
87
91
 
88
92
  def port
89
93
  return nil if sqlite?
90
94
 
91
- url_config['port'] || file_config['port'] || super
95
+ chained_config('port') || super
92
96
  end
93
97
 
94
98
  def username
95
- (
96
- url_config['username'] ||
97
- file_config['username'] ||
98
- (adapter && adapter.credentials['username'])
99
- )
99
+ chained_config('username') || (adapter && adapter.credentials['username'])
100
100
  end
101
101
 
102
102
  def password
103
- (
104
- url_config['password'] ||
105
- file_config['password'] ||
106
- (adapter && adapter.credentials['password'])
107
- )
103
+ chained_config('password') || (adapter && adapter.credentials['password'])
108
104
  end
109
105
 
110
106
  def database
111
- (
112
- url_config['database'] ||
113
- file_config['database'] ||
114
- (adapter && adapter.credentials['database'])
115
- )
107
+ chained_config('database') || (adapter && adapter.credentials['database'])
108
+ end
109
+
110
+ def chained_config(key)
111
+ return url_config[key] || file_config[key] if @env.environment == 'test'
112
+
113
+ file_config[key] || url_config[key]
116
114
  end
117
115
 
118
116
  def adapter_by_name(name)
@@ -121,7 +119,7 @@ module Orchestration
121
119
  'mysql' => adapters::Mysql2,
122
120
  'postgresql' => adapters::Postgresql,
123
121
  'sqlite3' => adapters::Sqlite3
124
- }.fetch(name).new
122
+ }.fetch(name).new(self)
125
123
  rescue KeyError
126
124
  Orchestration.error('database.unknown_adapter', adapter: name.inspect)
127
125
  raise
@@ -53,7 +53,7 @@ module Orchestration
53
53
  local, remote = parse_port(service).map(&:to_i)
54
54
  return remote if @env.environment == 'test' && @options[:sidecar]
55
55
 
56
- (@env.environment == 'production' ? remote : local)
56
+ (@env.environment == 'deployment' ? remote : local)
57
57
  end
58
58
 
59
59
  def service
@@ -4,6 +4,7 @@ module Orchestration
4
4
  module Services
5
5
  module RabbitMQ
6
6
  PORT = 5672
7
+ MANAGEMENT_PORT = 15_672
7
8
  end
8
9
  end
9
10
  end
@@ -2,7 +2,7 @@ FROM ruby:<%= ruby_version %>
2
2
  ARG BUNDLE_BITBUCKET__ORG
3
3
  ARG BUNDLE_GITHUB__COM
4
4
  ARG GIT_COMMIT
5
- RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
5
+ RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
6
6
  && apt-get update \
7
7
  && DEBIAN_FRONTEND=noninteractive apt-get install -y \
8
8
  nodejs \
@@ -13,17 +13,15 @@ RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
13
13
  && mkdir /app<%if defined?(Webpacker) %> \
14
14
  && curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash \
15
15
  && . /root/.bashrc \
16
- && nvm install 10.13.0 \
16
+ && nvm install 14.16.0 \
17
17
  && npm config set user 0 \
18
18
  && npm config set unsafe-perm true \
19
19
  && npm install -g yarn<% end %>
20
20
  WORKDIR /app
21
21
  COPY .build/Gemfile .build/Gemfile.lock ./
22
- RUN bundle install --without development test --deployment
23
- <% if defined?(Webpacker) %>
24
- COPY .build/package.json .build/yarn.lock ./
25
- RUN . /root/.bashrc ; yarn install
26
- <% end %>
22
+ RUN bundle config set deployment 'true' \
23
+ && bundle config set without 'development test' \
24
+ && bundle install
27
25
  ADD .build/context.tar .
28
26
  <% if defined?(Webpacker) %>RUN . /root/.bashrc ; NODE_ENV=production RAILS_ENV=production yarn install && NODE_ENV=production RAILS_ENV=production SECRET_KEY_BASE=abc123 bundle exec rake assets:precompile<% elsif Rake::Task.tasks.map(&:name).include?('assets:precompile') %>RUN NODE_ENV=production RAILS_ENV=production SECRET_KEY_BASE=abc123 bundle exec rake assets:precompile<% end %>
29
27
  RUN echo "${GIT_COMMIT}" > /app/GIT_COMMIT
@@ -1,20 +1,36 @@
1
+ include $(shell bundle exec ruby -e 'require "orchestration/make"')
2
+
1
3
  #
2
4
  # Example test command
3
5
  #
4
- # This command will call `test-setup` before running your usual test pipeline.
6
+ # Define your test tasks here. The default command runs RSpec and Rubocop but
7
+ # you can feel free to add any other tasks you like.
5
8
  #
6
- # `test-setup` starts all containers specified in `docker-compose.test.yml`,
7
- # waits for them to be ready, and runs DB migrations.
9
+ # Set up your test dependencies and run tests by calling: `make setup test`
8
10
  #
9
- # In your CI environment, simply run `make test`.
11
+ # Subsequent test runs can skip the setup step and simply run: `make test`
10
12
  #
11
13
  .PHONY: test
12
- test: test-setup
14
+ test:
13
15
  bundle exec rspec
14
16
  bundle exec rubocop
15
17
 
16
- # Start development containers and create/migrate/seed database
17
- .PHONY: develop
18
+ #
19
+ # Define any custom setup that needs to take place before running tests.
20
+ # If the command exists, it will be called immediately after the `setup`
21
+ # command (which starts containers and sets up the database).
22
+ #
23
+ # This command can be deleted if it is not needed.
24
+ #
25
+ .PHONY: post-setup
26
+ post-setup:
27
+ @# Setup tasks that are not already provided by Orchestration go here.
28
+
29
+ #
30
+ # Launch all dependencies needed for a development environment and set up the
31
+ # development database.
32
+ #
33
+ .PHONY: setup
18
34
  develop:
19
35
  bundle install
20
36
  @$(MAKE) start env=test
@@ -1,12 +1,15 @@
1
1
  <% if compose.call('development').services.key?('rabbitmq') %>
2
2
  development:
3
- url: <%= "#{'<' + '%' + '='} ENV.fetch('RABBITMQ_URL', 'amqp://127.0.0.1:#{compose.call('development').local_port('rabbitmq')}') #{'%' + '>'}" %>
3
+ url: <%= "#{'<' + '%' + '='} ENV.fetch('RABBITMQ_URL', 'amqp://127.0.0.1:#{compose.call('development').local_port('rabbitmq', 5672)}') #{'%' + '>'}" %>
4
+ management_url: <%= "#{'<' + '%' + '='} ENV.fetch('RABBITMQ_MANAGEMENT_URL', 'http://guest:guest@127.0.0.1:#{compose.call('development').local_port('rabbitmq', 15672)}') #{'%' + '>'}" %>
4
5
  <% end %>
5
6
 
6
7
  <% if compose.call('test').services.key?('rabbitmq') %>
7
8
  test:
8
- url: <%= "#{'<' + '%' + '='} ENV.fetch('RABBITMQ_URL', 'amqp://127.0.0.1:#{compose.call('test').local_port('rabbitmq')}') #{'%' + '>'}" %>
9
+ url: <%= "#{'<' + '%' + '='} ENV.fetch('RABBITMQ_URL', 'amqp://127.0.0.1:#{compose.call('test').local_port('rabbitmq', 5672)}') #{'%' + '>'}" %>
10
+ management_url: <%= "#{'<' + '%' + '='} ENV.fetch('RABBITMQ_MANAGEMENT_URL', 'http://guest:guest@127.0.0.1:#{compose.call('test').local_port('rabbitmq', 15672)}') #{'%' + '>'}" %>
9
11
  <% end %>
10
12
 
11
13
  production:
12
14
  url: <%%= ENV['RABBITMQ_URL'] %>
15
+ url: <%%= ENV['RABBITMQ_MANAGEMENT_URL'] %>
@@ -4,7 +4,6 @@ module Orchestration
4
4
  COLOR_MAP = {
5
5
  failure: %i[red bright],
6
6
  error: %i[red],
7
- waiting: %i[yellow],
8
7
  ready: %i[green],
9
8
  create: %i[green],
10
9
  update: %i[yellow],
@@ -12,7 +11,9 @@ module Orchestration
12
11
  status: %i[blue],
13
12
  setup: %i[blue],
14
13
  input: %i[red],
15
- skip: %i[yellow bright]
14
+ skip: %i[yellow bright],
15
+ waiting: %i[yellow],
16
+ config: %i[cyan]
16
17
  }.freeze
17
18
 
18
19
  class Terminal
@@ -20,7 +21,7 @@ module Orchestration
20
21
  @settings = settings
21
22
  end
22
23
 
23
- def write(desc, message, color_name = nil, newline: true)
24
+ def write(desc, message = nil, color_name = nil, newline: true)
24
25
  output = newline ? "#{message}\n" : message.to_s
25
26
  $stdout.print colorize(desc, output, color_name)
26
27
  $stdout.flush
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Orchestration
4
- VERSION = '0.5.11'
4
+ VERSION = '0.6.1'
5
5
  end
@@ -8,11 +8,9 @@ namespace :orchestration do
8
8
  Orchestration::InstallGenerator.start
9
9
  end
10
10
 
11
- namespace :install do
12
- desc I18n.t('orchestration.rake.install_makefile')
13
- task :makefile do
14
- Orchestration::InstallGenerator.new.orchestration_makefile
15
- end
11
+ desc I18n.t('orchestration.makefile')
12
+ task :makefile do
13
+ Orchestration.makefile
16
14
  end
17
15
 
18
16
  desc I18n.t('orchestration.rake.config')
@@ -21,6 +19,26 @@ namespace :orchestration do
21
19
  puts "#{config['docker']['organization']} #{config['docker']['repository']}"
22
20
  end
23
21
 
22
+ namespace :db do
23
+ desc I18n.t('orchestration.rake.db.url')
24
+ task :url do
25
+ config = Rails.application.config_for(:database)
26
+
27
+ if config[:adapter] == 'sqlite3'
28
+ puts "sqlite3:#{config[:database]}"
29
+ else
30
+ puts DatabaseUrl.to_active_record_url(config)
31
+ end
32
+ end
33
+
34
+ desc I18n.t('orchestration.rake.db.console')
35
+ task :console do
36
+ env = Orchestration::Environment.new
37
+ options = ENV['db'] ? { config_path: "config/database.#{ENV['db']}.yml" } : {}
38
+ sh Orchestration::Services::Database::Configuration.new(env, nil, options).console_command
39
+ end
40
+ end
41
+
24
42
  desc I18n.t('orchestration.rake.healthcheck')
25
43
  task :healthcheck do
26
44
  Orchestration::DockerHealthcheck.execute
@@ -28,7 +46,6 @@ namespace :orchestration do
28
46
 
29
47
  desc I18n.t('orchestration.rake.wait')
30
48
  task :wait do
31
- Orchestration::InstallGenerator.new.verify_makefile(skip: false)
32
49
  env = Orchestration::Environment.new
33
50
  services = Orchestration::Services
34
51
  env.docker_compose_config['services'].each do |name, _service|