orchestration 0.3.3 → 0.3.4

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 (40) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/TODO +2 -12
  4. data/config/locales/en.yml +10 -13
  5. data/lib/orchestration/docker_compose/{application_service.rb → app_service.rb} +6 -5
  6. data/lib/orchestration/docker_compose/configuration.rb +69 -0
  7. data/lib/orchestration/docker_compose/database_service.rb +18 -25
  8. data/lib/orchestration/docker_compose/install_generator.rb +113 -0
  9. data/lib/orchestration/docker_compose/mongo_service.rb +15 -5
  10. data/lib/orchestration/docker_compose/nginx_proxy_service.rb +3 -2
  11. data/lib/orchestration/docker_compose/rabbitmq_service.rb +9 -5
  12. data/lib/orchestration/docker_compose.rb +3 -2
  13. data/lib/orchestration/environment.rb +32 -11
  14. data/lib/orchestration/errors.rb +3 -1
  15. data/lib/orchestration/file_helpers.rb +10 -5
  16. data/lib/orchestration/install_generator.rb +38 -51
  17. data/lib/orchestration/services/{application → app}/configuration.rb +5 -5
  18. data/lib/orchestration/services/{application → app}/healthcheck.rb +4 -4
  19. data/lib/orchestration/services/app.rb +13 -0
  20. data/lib/orchestration/services/configuration_base.rb +3 -1
  21. data/lib/orchestration/services/database/adapters/mysql2.rb +19 -0
  22. data/lib/orchestration/services/database/adapters/postgresql.rb +20 -0
  23. data/lib/orchestration/services/database/adapters/sqlite3.rb +4 -0
  24. data/lib/orchestration/services/database/configuration.rb +23 -13
  25. data/lib/orchestration/services/healthcheck_base.rb +1 -1
  26. data/lib/orchestration/services/nginx_proxy/configuration.rb +7 -2
  27. data/lib/orchestration/services.rb +1 -1
  28. data/lib/orchestration/templates/Makefile.erb +99 -40
  29. data/lib/orchestration/templates/application.mk.erb +14 -0
  30. data/lib/orchestration/templates/deploy.mk.erb +34 -0
  31. data/lib/orchestration/templates/docker-compose.override.yml.erb +1 -0
  32. data/lib/orchestration/templates/env.erb +6 -0
  33. data/lib/orchestration/templates/unicorn.rb.erb +1 -1
  34. data/lib/orchestration/terminal.rb +17 -1
  35. data/lib/orchestration/version.rb +1 -1
  36. data/lib/orchestration.rb +2 -1
  37. data/lib/tasks/orchestration.rake +7 -17
  38. metadata +13 -8
  39. data/lib/orchestration/docker_compose/services.rb +0 -59
  40. data/lib/orchestration/services/application.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e49d1834028ab1854b554f998dcd5779e5af3474
4
- data.tar.gz: 35185f308b9bc16794a906f9b7e4bfec8b790355
2
+ SHA256:
3
+ metadata.gz: 416fd08805e2ce12b2397a91f5baf894130f26d1ddf047f11a6b788b89783f16
4
+ data.tar.gz: 7b2f05787d2173d2e160a4ec40a4359e12f9e0d108384c7bd13c6fba43118692
5
5
  SHA512:
6
- metadata.gz: 9f1b206a4458192418ef09dcac765fc1fc3c85b94b0f724f6009eadd20732f8e08cd9415395ce349740857a1a9f87a236f585f6dda77e03ba1838fe52bb200bd
7
- data.tar.gz: e76e69bdf49ec909149510d8664776c1ff0c91e92a7f26eabeff70969bf987fc9910896127d9eac6ae7bca94c48fcee9fca6eed1e8a68150ecb261b98e6a1030
6
+ metadata.gz: 7a48ebb75ada7116256385d8bcf4d7db9a3bc6a9236b56b641078148a79b4fbb60cf75d4a488d0141415857d85f1d222d99adfd1212dd6346f7a7f6f14c7dbbc
7
+ data.tar.gz: f9df0d2800ea58090177d05b146236c44aff5e7cbbdd407b10f99214bea3fb8ae5de69f3893fd52eafa996491132cba9a25ecd7119c24320a4d420400e469396
data/README.md CHANGED
@@ -18,7 +18,7 @@ Containers are automatically created for the following dependencies:
18
18
  Add this line to your application's Gemfile:
19
19
 
20
20
  ```ruby
21
- gem 'orchestration', '~> 0.3.3'
21
+ gem 'orchestration', '~> 0.3.4'
22
22
  ```
23
23
 
24
24
  And then build your bundle:
data/TODO CHANGED
@@ -1,5 +1,3 @@
1
- Provide volumes for databases and mount appropriate directories for adapter
2
-
3
1
  Refactor docker-compose services - these really belong in
4
2
  lib/orchestration/services/<service-name>/docker_compose.rb
5
3
 
@@ -7,13 +5,5 @@ Standardise on log formats - by policy or recommendation ?
7
5
 
8
6
  Redis support
9
7
 
10
- Stop supporting development mode. Provide a docker-compose.yml for loading
11
- development and test dependencies and expect user to run Rails application
12
- locally.
13
-
14
- Build a docker-compose.production.yml for running application in
15
- production (i.e. including application as a service and removing local port
16
- bindings).
17
-
18
- Make docker-compose file additive - detect existing services and add new ones
19
- rather than overwriting.
8
+ Use `Paint` instead of `Colorize` for colouring output - better licensing and no
9
+ monkeypatching `String` etc.
@@ -3,20 +3,21 @@ en:
3
3
  attempt_limit: "Unable to reconnect after %{limit} attempts. Aborting."
4
4
  default: "default"
5
5
 
6
- application:
7
- waiting: "Waiting for application: %{config}"
8
- ready: "Application is ready."
9
- connection_error: "Error attempting to connect to application: received status code %{code}"
6
+ app:
7
+ waiting: "Waiting for app: %{config}"
8
+ ready: "App is ready."
9
+ connection_error: "Error attempting to connect to app: received status code %{code}"
10
10
 
11
11
  database:
12
12
  waiting: "Waiting for database: %{config}"
13
13
  ready: "Database is ready."
14
+ unknown_environment: "Environment not defined in database configuration: %{environment}"
14
15
 
15
16
  mongo:
16
17
  waiting: "Waiting for Mongo: %{config}"
17
18
  ready: "Mongo is ready."
18
19
 
19
- nginx-proxy:
20
+ nginx_proxy:
20
21
  waiting: "Waiting for Nginx proxy: %{config}"
21
22
  ready: "Nginx proxy is ready."
22
23
 
@@ -39,11 +40,8 @@ en:
39
40
  prompt: "project name"
40
41
 
41
42
  rake:
42
- docker:
43
- username: "Output configured Docker username"
44
-
45
- application:
46
- wait: "Wait for database to become available"
43
+ app:
44
+ wait: "Wait for app to become available"
47
45
 
48
46
  database:
49
47
  wait: "Wait for database to become available"
@@ -54,11 +52,10 @@ en:
54
52
  mongo:
55
53
  wait: "Wait for Mongo to become available"
56
54
 
57
- nginx-proxy:
55
+ nginx_proxy:
58
56
  wait: "Wait for Nginx proxy to become available"
59
57
 
60
58
  rabbitmq:
61
59
  wait: "Wait for RabbitMQ to become available"
62
60
 
63
- rabbitmq:
64
- wait: "Initialise boilerplate for adding Docker to your application"
61
+ install: "Install Orchestration tools"
@@ -2,8 +2,9 @@
2
2
 
3
3
  module Orchestration
4
4
  module DockerCompose
5
- class ApplicationService
6
- def initialize(config)
5
+ class AppService
6
+ def initialize(config, environment)
7
+ @environment = environment
7
8
  @config = config
8
9
  end
9
10
 
@@ -27,8 +28,7 @@ module Orchestration
27
28
  'UNICORN_PRELOAD_APP' => '1',
28
29
  'UNICORN_TIMEOUT' => '60',
29
30
  'UNICORN_WORKER_PROCESSES' => '8',
30
- 'VIRTUAL_PORT' => '8080',
31
- 'VIRTUAL_HOST' => 'localhost'
31
+ 'VIRTUAL_PORT' => '8080'
32
32
  }.merge(inherited_environment)
33
33
  end
34
34
 
@@ -36,7 +36,8 @@ module Orchestration
36
36
  {
37
37
  'HOST_UID' => nil,
38
38
  'RAILS_ENV' => nil,
39
- 'SECRET_KEY_BASE' => nil
39
+ 'SECRET_KEY_BASE' => nil,
40
+ 'VIRTUAL_HOST' => nil
40
41
  }
41
42
  end
42
43
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orchestration
4
+ module DockerCompose
5
+ class Configuration
6
+ def initialize(env, environment, selected_services)
7
+ @env = env # Global environment
8
+ @environment = environment # Current build environment
9
+ @selected_services = selected_services
10
+ end
11
+
12
+ def version
13
+ @env.docker_api_version
14
+ end
15
+
16
+ def services
17
+ Hash[services_enabled]
18
+ end
19
+
20
+ def volumes
21
+ public_volume.merge(database_volume).merge(mongo_volume)
22
+ end
23
+
24
+ private
25
+
26
+ def services_available
27
+ {
28
+ app: AppService,
29
+ database: DatabaseService,
30
+ mongo: MongoService,
31
+ rabbitmq: RabbitMQService,
32
+ nginx_proxy: NginxProxyService
33
+ }
34
+ end
35
+
36
+ def services_enabled
37
+ @selected_services.map do |service, config|
38
+ definition = service_definition(service, config)
39
+ next if definition.nil?
40
+
41
+ [service.to_s, definition]
42
+ end.compact
43
+ end
44
+
45
+ def public_volume
46
+ { @env.public_volume => {} }
47
+ end
48
+
49
+ def database_volume
50
+ return {} unless services.key?('database')
51
+
52
+ { @env.database_volume => {} }
53
+ end
54
+
55
+ def mongo_volume
56
+ return {} unless services.key?('mongo')
57
+
58
+ { @env.mongo_volume => {} }
59
+ end
60
+
61
+ def service_definition(service, config)
62
+ services_available
63
+ .fetch(service)
64
+ .new(config, @environment)
65
+ .definition
66
+ end
67
+ end
68
+ end
69
+ end
@@ -7,44 +7,37 @@ module Orchestration
7
7
  # container to simplify port mapping.
8
8
  PORT = 3354
9
9
 
10
- def initialize(config)
10
+ def initialize(config, environment)
11
+ @environment = environment
11
12
  @config = config
12
13
  end
13
14
 
14
15
  def definition
15
16
  return nil if @config.settings.nil?
17
+ return nil if @config.adapter.name == 'sqlite3'
16
18
 
17
- adapter = @config.settings.fetch('adapter')
18
- return nil if adapter == 'sqlite3'
19
-
20
- port = @config.settings.fetch('port')
21
19
  {
22
- 'image' => image_from_adapter(adapter),
23
- 'environment' => environment_from_adapter(adapter),
24
- 'ports' => ["#{port}:#{PORT}"]
25
- }
20
+ 'image' => @config.adapter.image,
21
+ 'environment' => @config.adapter.environment
22
+ }.merge(ports).merge(volumes)
26
23
  end
27
24
 
28
25
  private
29
26
 
30
- def image_from_adapter(adapter)
31
- {
32
- 'postgresql' => 'library/postgres',
33
- 'mysql2' => 'library/mysql'
34
- }.fetch(adapter)
27
+ def volume
28
+ @config.env.database_volume
35
29
  end
36
30
 
37
- def environment_from_adapter(adapter)
38
- {
39
- 'postgresql' => {
40
- 'PGPORT' => PORT.to_s,
41
- 'POSTGRES_PASSWORD' => 'password'
42
- },
43
- 'mysql2' => {
44
- 'MYSQL_ROOT_PASSWORD' => 'password',
45
- 'MYSQL_TCP_PORT' => PORT.to_s
46
- }
47
- }.fetch(adapter)
31
+ def ports
32
+ return {} unless %i[development test].include?(@environment)
33
+
34
+ { 'ports' => ["#{@config.settings.fetch('port')}:#{PORT}"] }
35
+ end
36
+
37
+ def volumes
38
+ return {} if @environment == :test
39
+
40
+ { 'volumes' => ["#{volume}:#{@config.adapter.data_dir}"] }
48
41
  end
49
42
  end
50
43
  end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orchestration
4
+ module DockerCompose
5
+ class InstallGenerator
6
+ include FileHelpers
7
+
8
+ def initialize(env, terminal)
9
+ @env = env
10
+ @terminal = terminal
11
+ end
12
+
13
+ def docker_compose_yml
14
+ create_compose_file
15
+ end
16
+
17
+ def docker_compose_test_yml
18
+ create_compose_file(:test)
19
+ end
20
+
21
+ def docker_compose_development_yml
22
+ create_compose_file(:development)
23
+ end
24
+
25
+ def docker_compose_production_yml
26
+ create_compose_file(:production)
27
+ end
28
+
29
+ def docker_compose_override_yml
30
+ simple_copy(
31
+ 'docker-compose.override.yml',
32
+ @env.docker_compose_path(:override),
33
+ overwrite: false
34
+ )
35
+ end
36
+
37
+ def enabled_services(environment)
38
+ service_names(environment).select { |name| service_enabled?(name) }
39
+ end
40
+
41
+ private
42
+
43
+ def create_compose_file(environment = nil)
44
+ path = @env.docker_compose_path(environment)
45
+ create_file(
46
+ path,
47
+ structure(environment).to_yaml,
48
+ overwrite: false
49
+ )
50
+ end
51
+
52
+ def structure(environment = nil)
53
+ {
54
+ 'version' => compose_config(environment).version,
55
+ 'services' => services(environment)
56
+ }.merge('volumes' => volumes(environment))
57
+ end
58
+
59
+ def services(environment)
60
+ compose_config(environment).services
61
+ end
62
+
63
+ def volumes(environment)
64
+ return {} if environment.nil? || environment == :test
65
+
66
+ compose_config(environment).volumes
67
+ end
68
+
69
+ def compose_config(environment)
70
+ DockerCompose::Configuration.new(
71
+ @env,
72
+ environment,
73
+ Hash[configurations(environment)]
74
+ )
75
+ end
76
+
77
+ def service_names(environment)
78
+ case environment
79
+ when :test, :development
80
+ %i[database mongo rabbitmq]
81
+ when :production
82
+ %i[nginx_proxy app database mongo rabbitmq]
83
+ when nil
84
+ []
85
+ else
86
+ raise ArgumentError, environment.inspect
87
+ end
88
+ end
89
+
90
+ def configurations(environment)
91
+ service_names(environment).map do |key|
92
+ [key, configuration(key)]
93
+ end
94
+ end
95
+
96
+ def configuration(service)
97
+ {
98
+ app: Orchestration::Services::App::Configuration,
99
+ database: Orchestration::Services::Database::Configuration,
100
+ mongo: Orchestration::Services::Mongo::Configuration,
101
+ rabbitmq: Orchestration::Services::RabbitMQ::Configuration,
102
+ nginx_proxy: Orchestration::Services::NginxProxy::Configuration
103
+ }.fetch(service).new(@env)
104
+ end
105
+
106
+ def service_enabled?(service_name)
107
+ return false if configuration(service_name).settings.nil?
108
+
109
+ true
110
+ end
111
+ end
112
+ end
113
+ end
@@ -5,21 +5,31 @@ module Orchestration
5
5
  class MongoService
6
6
  PORT = 27_020
7
7
 
8
- def initialize(config)
8
+ def initialize(config, environment)
9
9
  @config = config
10
+ @environment = environment
10
11
  end
11
12
 
12
13
  def definition
13
14
  return nil if @config.settings.nil?
14
15
 
15
- {
16
- 'image' => 'library/mongo',
17
- 'ports' => ["#{local_port}:#{remote_port}"]
18
- }
16
+ { 'image' => 'library/mongo' }.merge(ports).merge(volumes)
19
17
  end
20
18
 
21
19
  private
22
20
 
21
+ def ports
22
+ return {} unless %i[development test].include?(@environment)
23
+
24
+ { 'ports' => ["#{local_port}:#{remote_port}"] }
25
+ end
26
+
27
+ def volumes
28
+ return {} if @environment == :test
29
+
30
+ { 'volumes' => ["#{@config.env.mongo_volume}:/data/db"] }
31
+ end
32
+
23
33
  def local_port
24
34
  _host, _, port = clients.fetch('default')
25
35
  .fetch('hosts')
@@ -3,14 +3,15 @@
3
3
  module Orchestration
4
4
  module DockerCompose
5
5
  class NginxProxyService
6
- def initialize(config)
6
+ def initialize(config, environment)
7
+ @environment = environment
7
8
  @config = config
8
9
  end
9
10
 
10
11
  def definition
11
12
  {
12
13
  'image' => 'rubyorchestration/nginx-proxy',
13
- 'ports' => %w[3000:80],
14
+ 'ports' => %w[${LISTEN_PORT}:80],
14
15
  'volumes' => [
15
16
  '/var/run/docker.sock:/tmp/docker.sock:ro',
16
17
  "#{@config.env.public_volume}:/var/www/public/:ro"
@@ -3,20 +3,24 @@
3
3
  module Orchestration
4
4
  module DockerCompose
5
5
  class RabbitMQService
6
- def initialize(config)
6
+ def initialize(config, environment)
7
7
  @config = config
8
+ @environment = environment
8
9
  end
9
10
 
10
11
  def definition
11
12
  return nil if @config.settings.nil?
12
13
 
14
+ { 'image' => 'library/rabbitmq' }.merge(ports)
15
+ end
16
+
17
+ def ports
18
+ return {} unless %i[development test].include?(@environment)
19
+
13
20
  host_port = @config.settings.fetch('port', 5672)
14
21
  container_port = Orchestration::Services::RabbitMQ::PORT
15
22
 
16
- {
17
- 'image' => 'library/rabbitmq',
18
- 'ports' => ["#{host_port}:#{container_port}"]
19
- }
23
+ { 'ports' => ["#{host_port}:#{container_port}"] }
20
24
  end
21
25
  end
22
26
  end
@@ -5,9 +5,10 @@ module Orchestration
5
5
  end
6
6
  end
7
7
 
8
- require 'orchestration/docker_compose/services'
8
+ require 'orchestration/docker_compose/install_generator'
9
+ require 'orchestration/docker_compose/configuration'
9
10
 
10
- require 'orchestration/docker_compose/application_service'
11
+ require 'orchestration/docker_compose/app_service'
11
12
  require 'orchestration/docker_compose/database_service'
12
13
  require 'orchestration/docker_compose/mongo_service'
13
14
  require 'orchestration/docker_compose/nginx_proxy_service'
@@ -32,23 +32,36 @@ module Orchestration
32
32
  root.join('.orchestration.yml')
33
33
  end
34
34
 
35
- def docker_compose_configuration_path
36
- orchestration_root.join('docker-compose.yml')
35
+ def docker_api_version
36
+ '3.7'
37
37
  end
38
38
 
39
- def docker_compose_config
40
- YAML.safe_load(File.read(docker_compose_configuration_path))
39
+ def docker_compose_path(env = nil)
40
+ return orchestration_root.join('docker-compose.yml') if env.nil?
41
+
42
+ orchestration_root.join("docker-compose.#{env}.yml")
43
+ end
44
+
45
+ def docker_compose_config(env = nil)
46
+ env ||= environment
47
+ YAML.safe_load(File.read(docker_compose_path(env)))
41
48
  end
42
49
 
43
- def docker_compose_config?
44
- docker_compose_configuration_path.file?
50
+ def docker_compose_config?(env = nil)
51
+ env ||= environment
52
+ docker_compose_path(env).file?
45
53
  end
46
54
 
47
- def default_application_name
55
+ def default_app_name
56
+ default = root.basename.to_s
57
+ return default unless defined?(Rails)
58
+ # Edge case if Rails is used as a dependency but we are not a Rails app:
59
+ return default if Rails.application.class.parent == Object
60
+
48
61
  Rails.application.class.parent.name.underscore
49
62
  end
50
63
 
51
- def application_name
64
+ def app_name
52
65
  settings.get('docker.repository')
53
66
  end
54
67
 
@@ -57,8 +70,8 @@ module Orchestration
57
70
  end
58
71
 
59
72
  def root
60
- return Rails.root if defined?(Rails) && Rails.root
61
-
73
+ defined?(Rails) && Rails.root ? Rails.root : Pathname.new(Dir.pwd)
74
+ rescue NoMethodError
62
75
  Pathname.new(Dir.pwd)
63
76
  end
64
77
 
@@ -71,7 +84,15 @@ module Orchestration
71
84
  end
72
85
 
73
86
  def public_volume
74
- "#{application_name}_public"
87
+ "#{app_name}_public"
88
+ end
89
+
90
+ def database_volume
91
+ "#{app_name}_database"
92
+ end
93
+
94
+ def mongo_volume
95
+ "#{app_name}_mongo"
75
96
  end
76
97
  end
77
98
  end
@@ -3,7 +3,9 @@
3
3
  module Orchestration
4
4
  class OrchestrationError < StandardError; end
5
5
 
6
- class ApplicationConnectionError < OrchestrationError; end
6
+ class AppConnectionError < OrchestrationError; end
7
7
  class DatabaseConfigurationError < OrchestrationError; end
8
8
  class MongoConfigurationError < OrchestrationError; end
9
+
10
+ class UnknownEnvironmentError < DatabaseConfigurationError; end
9
11
  end
@@ -27,11 +27,15 @@ module Orchestration
27
27
  end
28
28
 
29
29
  def relative_path(path)
30
- path.relative_path_from(Rails.root).to_s
30
+ path.relative_path_from(@env.root).to_s
31
31
  end
32
32
 
33
- def simple_copy(template_name, dest)
34
- update_file(dest, template(template_name))
33
+ def simple_copy(template_name, dest, options = {})
34
+ update_file(
35
+ dest,
36
+ template(template_name, env: @env),
37
+ overwrite: options.fetch(:overwrite, true)
38
+ )
35
39
  end
36
40
 
37
41
  def create_file(path, content, options = {})
@@ -44,12 +48,13 @@ module Orchestration
44
48
  @terminal.write(:create, relative_path(path))
45
49
  end
46
50
 
47
- def update_file(path, content)
51
+ def update_file(path, content, options = {})
48
52
  present = File.exist?(path)
49
53
  return create_file(path, content) unless present
50
54
 
55
+ overwrite = options.fetch(:overwrite, true)
51
56
  previous_content = File.read(path) if present
52
- if present && previous_content == content
57
+ if present && (!overwrite || previous_content == content)
53
58
  return @terminal.write(:skip, relative_path(path))
54
59
  end
55
60