orchestration 0.5.11 → 0.6.1

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.
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|