ruby_yacht 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +1 -1
  3. data/doc/TODO.md +16 -6
  4. data/doc/configuration_sample.rb +8 -9
  5. data/lib/ruby_yacht/dsl/app.rb +29 -1
  6. data/lib/ruby_yacht/dsl/app_type.rb +112 -0
  7. data/lib/ruby_yacht/dsl/configuration.rb +163 -2
  8. data/lib/ruby_yacht/dsl/dsl.rb +51 -9
  9. data/lib/ruby_yacht/dsl/hook.rb +105 -0
  10. data/lib/ruby_yacht/dsl/project.rb +17 -21
  11. data/lib/ruby_yacht/dsl.rb +2 -0
  12. data/lib/ruby_yacht/images/app/Dockerfile.erb +20 -13
  13. data/lib/ruby_yacht/images/app/before_startup.bash.erb +8 -0
  14. data/lib/ruby_yacht/images/app/checkout.bash +6 -0
  15. data/lib/ruby_yacht/images/app/startup.bash.erb +5 -0
  16. data/lib/ruby_yacht/images/app-dependencies/Dockerfile.erb +23 -5
  17. data/lib/ruby_yacht/images/database/Dockerfile.erb +25 -11
  18. data/lib/ruby_yacht/images/database/checkout.bash +10 -0
  19. data/lib/ruby_yacht/images/database/setup.bash +15 -0
  20. data/lib/ruby_yacht/images/web/Dockerfile.erb +4 -3
  21. data/lib/ruby_yacht/plugins/rails/scripts/install_gems.rb +3 -0
  22. data/lib/ruby_yacht/plugins/rails/scripts/load_seeds.rb +34 -0
  23. data/lib/ruby_yacht/{images/app/startup.rb → plugins/rails/scripts/prepare_rails_for_launch.rb} +2 -8
  24. data/lib/ruby_yacht/{images/app/update_database_config.rb → plugins/rails/scripts/update_rails_config.rb} +0 -1
  25. data/lib/ruby_yacht/plugins/rails.rb +40 -0
  26. data/lib/ruby_yacht/plugins.rb +6 -0
  27. data/lib/ruby_yacht/runner/build_images.rb +72 -5
  28. data/lib/ruby_yacht/runner/checkout.rb +1 -1
  29. data/lib/ruby_yacht.rb +2 -1
  30. data/ruby_yacht.gemspec +2 -1
  31. data/spec/docker/Dockerfile +1 -1
  32. data/spec/dsl/app_spec.rb +34 -0
  33. data/spec/dsl/app_type_spec.rb +176 -0
  34. data/spec/dsl/configuration_spec.rb +161 -2
  35. data/spec/dsl/dsl_spec.rb +3 -3
  36. data/spec/dsl/hook_spec.rb +111 -0
  37. data/spec/dsl/project_spec.rb +19 -83
  38. data/spec/fixtures/app-dependencies-dockerfile-generic +21 -0
  39. data/spec/fixtures/app-dependencies-dockerfile-generic-with-library-install +28 -0
  40. data/spec/fixtures/app-dependencies-dockerfile-rails +32 -0
  41. data/spec/fixtures/database-dockerfile +16 -15
  42. data/spec/fixtures/database-dockerfile-rails +35 -0
  43. data/spec/fixtures/database-dockerfile-with-seed-hooks +34 -0
  44. data/spec/fixtures/hooks/hook1.rb +0 -0
  45. data/spec/fixtures/hooks/hook2.rb +0 -0
  46. data/spec/fixtures/mars-before-startup +4 -0
  47. data/spec/fixtures/mars-before-startup-rails +7 -0
  48. data/spec/fixtures/mars-before-startup-with-before-startup-hooks +7 -0
  49. data/spec/fixtures/mars-dockerfile +9 -14
  50. data/spec/fixtures/mars-dockerfile-rails +33 -0
  51. data/spec/fixtures/mars-dockerfile-with-after-checkout-hooks +30 -0
  52. data/spec/fixtures/mars-dockerfile-with-before-startup-hooks +29 -0
  53. data/spec/fixtures/mars-startup +3 -0
  54. data/spec/fixtures/mars-startup-rails +3 -0
  55. data/spec/fixtures/multi-project-web-dockerfile +0 -10
  56. data/spec/fixtures/web-dockerfile +0 -6
  57. data/spec/plugins/rails_spec.rb +198 -0
  58. data/spec/runner/build_images_spec.rb +181 -15
  59. data/spec/runner/checkout_spec.rb +2 -2
  60. data/spec/support/test_project.rb +16 -8
  61. metadata +62 -30
  62. data/lib/ruby_yacht/images/app/checkout.rb +0 -7
  63. data/lib/ruby_yacht/images/app-dependencies/install_gems.rb +0 -12
  64. data/lib/ruby_yacht/images/database/load_seeds.rb +0 -42
  65. data/lib/ruby_yacht/images/database/setup.rb +0 -19
  66. data/lib/ruby_yacht/images/database/setup_database.sql.erb +0 -12
  67. data/spec/fixtures/app-dependencies-dockerfile +0 -25
@@ -7,6 +7,8 @@ module RubyYacht
7
7
  #
8
8
  # A project is a family of apps that communicate with each other and
9
9
  # share a database.
10
+ #
11
+ # You can configure this with RubyYacht::Project::DSL
10
12
  class Project
11
13
  # The name of the project.
12
14
  attr_accessor :name
@@ -21,12 +23,6 @@ module RubyYacht
21
23
  # The hostname for the repository that contains the code for the apps.
22
24
  attr_accessor :repository
23
25
 
24
- # The secret key that the apps should use for signing cookies.
25
- attr_accessor :secret_key_base
26
-
27
- # The environment that the apps should run in.
28
- attr_accessor :rails_environment
29
-
30
26
  # Whether we should check out the code on the host machine, rather than
31
27
  # keeping it entirely inside the container.
32
28
  attr_accessor :check_out_locally
@@ -59,6 +55,8 @@ module RubyYacht
59
55
  # * *name: String* The name of the project
60
56
  def initialize(name)
61
57
  @name = name.to_sym
58
+ load_custom_attributes
59
+ load_app_initializers
62
60
  end
63
61
 
64
62
  add_attribute :name
@@ -75,27 +73,12 @@ module RubyYacht
75
73
  # test.com
76
74
  add_attribute :domain
77
75
 
78
- ##
79
- # :method: secret_key_base
80
- # You can call `secret_key_base '1234kj1234k;lj1234jklh'` to set the
81
- # project's `secret_key_base` to `1234kj1234k;lj1234jklh`.
82
- add_attribute :secret_key_base
83
-
84
76
  ##
85
77
  # :method: repository
86
78
  # You can call `repository 'github.com'` to set the project's `repository`
87
79
  # to `github.com`
88
80
  add_attribute :repository
89
81
 
90
- ##
91
- # :method: rails_environment
92
- #
93
- # You can call `rails_environment 'staging'` to set the project's
94
- # `rails_environment` to `staging`.
95
- #
96
- # The default is `development`.
97
- add_attribute :rails_environment, 'development'
98
-
99
82
  ##
100
83
  # :method: check_out_locally
101
84
  #
@@ -139,6 +122,19 @@ module RubyYacht
139
122
  add_object :dns_server, RubyYacht::DnsServer::DSL, required: false
140
123
 
141
124
  creates_object Project
125
+
126
+ # This method loads the special DSL methods for defining apps of a certain
127
+ # type.
128
+ #
129
+ # For the app type with the name `foo`, this will create a `foo_app`
130
+ # method on the DSL that builds an app with the type `foo`.
131
+ def load_app_initializers
132
+ RubyYacht.configuration.app_types.each do |app_type|
133
+ define_singleton_method "#{app_type.name}_app" do |name, &block|
134
+ app name, app_type.name, &block
135
+ end
136
+ end
137
+ end
142
138
  end
143
139
  end
144
140
  end
@@ -4,3 +4,5 @@ require 'ruby_yacht/dsl/project'
4
4
  require 'ruby_yacht/dsl/app'
5
5
  require 'ruby_yacht/dsl/database'
6
6
  require 'ruby_yacht/dsl/dns_server'
7
+ require 'ruby_yacht/dsl/hook'
8
+ require 'ruby_yacht/dsl/app_type'
@@ -1,32 +1,39 @@
1
- FROM <%= @project.system_prefix %>-app-dependencies
1
+ FROM <%= @project.system_prefix %>-<%= @app.app_type %>-app-dependencies
2
2
 
3
3
  ENV DATABASE_HOST <%= @project.database.host %>
4
4
  ENV DATABASE_NAME <%= @project.database.name %>
5
5
  ENV DATABASE_PASSWORD <%= @project.database.password %>
6
6
  ENV DATABASE_USERNAME <%= @project.database.username %>
7
- ENV RAILS_ENV <%= @project.rails_environment %>
8
- ENV RAILS_PORT <%= @app.port %>
7
+ ENV APP_PORT <%= @app.port %>
9
8
  ENV REPOSITORY_HOST <%= @project.repository %>
10
- ENV SECRET_KEY_BASE <%= @project.secret_key_base %>
11
9
  ENV SYSTEM_PREFIX <%= @project.system_prefix %>
10
+ <% app_type = RubyYacht.configuration.find_app_type(@app.app_type) %>
11
+ <% app_type.environment_variables.select { |variable| variable[:image] == :app }.each do |variable| %>
12
+ ENV <%= variable[:name] %> <%= instance_eval(&variable[:block]) %>
13
+ <% end %>
12
14
 
13
15
  RUN rm -r /var/code
14
16
  RUN mkdir -p /var/code
15
17
 
16
- COPY startup.rb /var/docker/startup.rb
17
- COPY checkout.rb /var/docker/checkout.rb
18
- COPY update_database_config.rb /var/docker/update_database_config.rb
19
-
20
- RUN chmod u+x /var/docker/*.rb
21
-
22
18
  ENV REPOSITORY_NAME <%= @app.repository_name %>
19
+ COPY checkout.bash /var/docker/checkout.bash
20
+ RUN bash /var/docker/checkout.bash
21
+
22
+ COPY before_startup.bash /var/docker/before_startup.bash
23
+ COPY startup.bash /var/docker/startup.bash
24
+ <% RubyYacht.configuration.fetch_hooks(app_type: @app.app_type).select { |hook| hook.script_name != '' }.each do |hook| %>
25
+ <% next unless [:startup, :build_checkout].include?(hook.event_type) %>
26
+ COPY <%= hook.script_name %> /var/docker/<%= hook.script_name %>
27
+ <% end %>
23
28
 
24
- RUN /var/docker/checkout.rb
29
+ RUN chmod u+x /var/docker/*
25
30
 
26
31
  WORKDIR /var/code
27
32
 
28
- RUN bundle install && bundle clean --force
33
+ <% RubyYacht.configuration.fetch_hooks(app_type: @app.app_type, event_time: :after, event_type: :build_checkout).each do |hook| %>
34
+ RUN <%= hook.command %>
35
+ <% end %>
29
36
 
30
37
  EXPOSE <%= @app.port %>
31
38
 
32
- CMD /var/docker/startup.rb
39
+ CMD /var/docker/startup.bash
@@ -0,0 +1,8 @@
1
+ #! /bin/bash
2
+
3
+ cd /var/code;
4
+ /var/docker/checkout.bash;
5
+
6
+ <% RubyYacht.configuration.fetch_hooks(app_type: @app.app_type, event_time: :before, event_type: :startup).each do |hook| %>
7
+ <%= hook.command %>;
8
+ <% end %>
@@ -0,0 +1,6 @@
1
+ #! /bin/bash
2
+
3
+ if [ ! -d /var/code/.git ]
4
+ then
5
+ git clone git@$REPOSITORY_HOST:$REPOSITORY_NAME /var/code
6
+ fi
@@ -0,0 +1,5 @@
1
+ #! /bin/bash
2
+ /var/docker/before_startup.bash;
3
+ <% RubyYacht.configuration.fetch_hooks(app_type: @app.app_type, event_time: :during, event_type: :startup).each do |hook| %>
4
+ <%= hook.command %>;
5
+ <% end %>
@@ -1,23 +1,41 @@
1
- # This Dockerfile creates an image for installing all of the gem dependencies
1
+ # This Dockerfile creates an image for installing all of the dependencies
2
2
  # for all of the apps.
3
3
  #
4
4
  # This is a slow process, so this should be kept as simple as possible so that
5
5
  # it can remain unchanged.
6
6
 
7
- FROM ruby:2.3
7
+ FROM <%= @app_type.baseline_image %>
8
8
 
9
9
  RUN mkdir -p /root/.ssh
10
10
  COPY id_rsa /root/.ssh
11
11
  RUN ssh-keyscan <%= @project.repository %> >> /root/.ssh/known_hosts
12
12
 
13
13
  RUN mkdir -p /var/docker/
14
- COPY install_gems.rb /var/docker/install_gems.rb
14
+
15
+ <% library_scripts = RubyYacht.configuration.fetch_hooks(app_type: @app_type.name, event_time: :during, event_type: :install_libraries).select { |hook| hook.script_name != '' } %>
16
+ <% library_scripts.each do |hook |%>
17
+ COPY <%= hook.script_name %> /var/docker/<%= hook.script_name %>
18
+ <% end %>
19
+
20
+ <% if library_scripts.any? %>
21
+ RUN chmod u+x /var/docker/*
22
+ <% end %>
15
23
 
16
24
  <% @project.apps.each do |app| %>
17
- RUN ruby /var/docker/install_gems.rb <%= app.name %> <%= @project.repository %> <%= app.repository_name %>
25
+ <% next unless app.app_type == @app_type.name %>
26
+
27
+ RUN git clone git@<%= @project.repository %>:<%= app.repository_name %> /var/code/<%= app.name %>
28
+ WORKDIR /var/code/<%= app.name %>
29
+ <% RubyYacht.configuration.fetch_hooks(app_type: @app_type.name, event_time: :during, event_type: :install_libraries).each do |hook |%>
30
+ RUN <%= hook.command %>
31
+ <% end %>
32
+ <% end %>
33
+
34
+ <% if library_scripts.any? %>
35
+ RUN rm /var/docker/*
18
36
  <% end %>
19
37
 
20
- RUN rm /var/docker/install_gems.rb
38
+ WORKDIR /
21
39
 
22
40
  RUN apt-get update && apt-get upgrade -y
23
41
  RUN apt-get install -y mysql-client
@@ -1,26 +1,40 @@
1
- FROM <%= @project.system_prefix %>-app-dependencies
2
-
1
+ FROM <%= @project.system_prefix %>-<%= @app_type.name %>-app-dependencies
3
2
  <% database = @project.database %>
4
3
 
5
4
  ENV DEBIAN_FRONTEND noninteractive
6
5
  RUN apt-get install -y mysql-server
7
6
 
8
- COPY setup.rb /var/docker/setup.rb
9
- COPY setup_database.sql.erb /var/docker/setup_database.sql.erb
10
- COPY load_seeds.rb /var/docker/load_seeds.rb
7
+ ENV DATABASE_USERNAME <%= @project.database.username %>
8
+ ENV DATABASE_PASSWORD <%= @project.database.password %>
9
+ ENV DATABASE_NAME <%= @project.database.name %>
10
+
11
+ COPY setup.bash /var/docker/setup.bash
12
+ COPY checkout.bash /var/docker/checkout.bash
13
+ <% @project.apps.map(&:app_type).uniq.each do |app_type| %>
14
+ <% RubyYacht.configuration.fetch_hooks(app_type: app_type, event_type: :load_database_seeds).each do |hook| %>
15
+ COPY <%= hook.script_name %> /var/docker/<%= hook.script_name %>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ RUN chmod u+x /var/docker/*
11
20
 
12
- RUN ruby /var/docker/setup.rb <%= database.name %> <%= database.username %> <%= database.password %> <%= @project.rails_environment %>
21
+ RUN /var/docker/setup.bash <%= database.name %> <%= database.username %> <%= database.password %>
13
22
 
14
- <% initialized = false %>
15
23
  <% @project.apps.each do |app| %>
16
- <% database_name = database.name %>
17
- <% database_name += '_development' if @project.rails_environment == 'development' %>
18
- RUN ruby /var/docker/load_seeds.rb <%= app.name %> <%= database_name %> <%= database.username %> <%= database.password %> <%= initialized %>
19
- <% initialized = true %>
24
+
25
+ RUN /var/docker/checkout.bash <%= @project.repository %> <%= app.name %> <%= app.repository_name %>
26
+ WORKDIR /var/code/<%= app.name %>
27
+ <% @app_type = RubyYacht.configuration.find_app_type(app.app_type) %>
28
+ <%= include_event :load_database_seeds %>
20
29
  <% end %>
30
+
21
31
  RUN rm -r /var/docker
22
32
  RUN rm -r /var/code
23
33
 
34
+ ENV DATABASE_USERNAME ''
35
+ ENV DATABASE_PASSWORD ''
36
+ ENV DATABASE_NAME ''
37
+
24
38
  EXPOSE 3306
25
39
 
26
40
  CMD mysqld_safe --bind-address=0.0.0.0
@@ -0,0 +1,10 @@
1
+ #! /bin/bash
2
+
3
+ REPOSITORY_HOST=$1
4
+ APP_NAME=$2
5
+ REPOSITORY_NAME=$3
6
+
7
+ if [ ! -d /var/code/$APP_NAME/.git ]
8
+ then
9
+ git clone git@$REPOSITORY_HOST:$REPOSITORY_NAME /var/code/$APP_NAME
10
+ fi
@@ -0,0 +1,15 @@
1
+ #! /bin/bash
2
+
3
+ DATABASE_NAME=$1
4
+ DATABASE_USER=$2
5
+ DATABASE_PASSWORD=$3
6
+
7
+ service mysql start;
8
+ COMMAND="CREATE USER '$DATABASE_USER'@'localhost' IDENTIFIED BY '$DATABASE_PASSWORD';";
9
+ COMMAND="$COMMAND CREATE USER '$DATABASE_USER'@'%' IDENTIFIED BY '$DATABASE_PASSWORD';";
10
+
11
+ COMMAND="$COMMAND GRANT ALL ON $DATABASE_NAME.* to '$DATABASE_USER'@'localhost';";
12
+ COMMAND="$COMMAND GRANT ALL ON $DATABASE_NAME.* to '$DATABASE_USER'@'%';";
13
+ COMMAND="$COMMAND CREATE DATABASE $DATABASE_NAME;";
14
+
15
+ mysql -uroot -e "$COMMAND";
@@ -13,11 +13,12 @@ COPY index_config.erb /var/docker/index_config.erb
13
13
  COPY app_config.erb /var/docker/app_config.erb
14
14
 
15
15
  <% @projects.each do |project| %>
16
- <% app_list = project.apps.map(&:name).join(',') %>
16
+
17
+ <% app_list = project.apps.map(&:name).join(',') %>
17
18
  RUN ruby /var/docker/add_project.rb <%= project.system_prefix %> <%= project.domain %> <%= app_list %>
18
- <% project.apps.each do |app| %>
19
+ <% project.apps.each do |app| %>
19
20
  RUN ruby /var/docker/add_app.rb <%= project.system_prefix %> <%= project.domain %> <%= app.name %> <%= app.port %>
20
- <% end %>
21
+ <% end %>
21
22
  <% end %>
22
23
 
23
24
  RUN rm -r /var/docker
@@ -0,0 +1,3 @@
1
+ #! /usr/local/bin/ruby
2
+
3
+ system "bundle install"
@@ -0,0 +1,34 @@
1
+ #! /usr/local/bin/ruby
2
+
3
+ require 'yaml'
4
+ require 'fileutils'
5
+
6
+ puts "Loading seeds"
7
+
8
+ system "service mysql start"
9
+
10
+ config_path = "./config/database.yml"
11
+
12
+ database_config = {
13
+ 'development' => {
14
+ 'adapter' => 'mysql2',
15
+ 'encoding' => 'utf8',
16
+ 'reconnect' => true,
17
+ 'username' => ENV['DATABASE_USERNAME'],
18
+ 'password' => ENV['DATABASE_PASSWORD'],
19
+ 'host' => 'localhost',
20
+ 'database' => ENV['DATABASE_NAME']
21
+ }
22
+ }
23
+
24
+ File.open(config_path, "w") do |file|
25
+ file.write(database_config.to_yaml)
26
+ end
27
+
28
+ system "bundle install"
29
+ tables = `mysql -uroot -e "SHOW TABLES" #{ENV['DATABASE_NAME']}`
30
+ if tables == ''
31
+ system "bundle exec rake db:reset"
32
+ else
33
+ system "bundle exec rake db:migrate; bundle exec rake db:seed"
34
+ end
@@ -1,17 +1,11 @@
1
1
  #! /usr/local/bin/ruby
2
2
 
3
3
  require 'fileutils'
4
- require_relative 'checkout'
5
- require_relative 'update_database_config'
6
-
7
- FileUtils.cd '/var/code'
8
4
 
9
5
  system "bundle install"
10
6
  system "rake db:migrate"
11
-
12
7
  system "rails r Rails.cache.clear"
8
+
13
9
  if ENV['RAILS_ENV'] != 'development'
14
10
  system "rake assets:precompile"
15
- end
16
-
17
- exec "rails s -b 0.0.0.0 -p $RAILS_PORT -e $RAILS_ENV"
11
+ end
@@ -23,7 +23,6 @@ database_config = {
23
23
  if ENV['RAILS_ENV'] == 'development' && local_database
24
24
  database_config['test'] = common_config.dup
25
25
  database_config['test']['database'] += '_test'
26
- database_config['development']['database'] += '_development'
27
26
  end
28
27
 
29
28
  File.open('/var/code/config/database.yml', 'w') do |file|
@@ -0,0 +1,40 @@
1
+ module RubyYacht::Plugins
2
+ # This module provides the plugin for managing Ruby on Rails apps.
3
+ module Rails
4
+ # This method loads the configuration for the Rails plugin.
5
+ def self.load
6
+ RubyYacht.configure do
7
+ app_type :rails do
8
+ baseline_image 'ruby:2.3'
9
+ project_attribute name: :environment, default: 'development'
10
+ project_attribute name: :secret_key_base
11
+
12
+ environment_variable :app, 'RAILS_ENV' do
13
+ @project.rails_environment
14
+ end
15
+
16
+ environment_variable :app, 'SECRET_KEY_BASE' do
17
+ @project.rails_secret_key_base
18
+ end
19
+ end
20
+ end
21
+
22
+ RubyYacht.configure do
23
+ add_hooks(app_type: :rails, folder: File.join(File.dirname(__FILE__), 'rails', 'scripts')) do
24
+ during(:install_libraries) { run_script 'install_gems.rb' }
25
+
26
+ after(:build_checkout) { command 'bundle install && bundle clean --force' }
27
+
28
+ during(:load_database_seeds) { run_script 'load_seeds.rb' }
29
+
30
+ before(:startup) { run_script 'update_rails_config.rb' }
31
+ before(:startup) { run_script 'prepare_rails_for_launch.rb' }
32
+
33
+ during(:startup) { command 'rails s -b 0.0.0.0 -p $APP_PORT -e $RAILS_ENV' }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ RubyYacht::Plugins::Rails.load
@@ -0,0 +1,6 @@
1
+ module RubyYacht
2
+ # This module groups plugins that can customize the build behavior.
3
+ module Plugins
4
+ end
5
+ end
6
+ require 'ruby_yacht/plugins/rails'
@@ -28,13 +28,19 @@ module RubyYacht::Runner
28
28
  FileUtils.cp(id_path, tmp_id_path)
29
29
  end
30
30
 
31
- build_image 'app-dependencies'
31
+ project.apps.map(&:app_type).uniq.each do |type_name|
32
+ @app_type = RubyYacht.configuration.find_app_type(type_name)
33
+ build_image 'app-dependencies', "#{@project.system_prefix}-#{@app_type.name}-app-dependencies"
34
+ end
35
+
32
36
  build_image 'database' if project.database.local?
33
37
 
34
38
  @project.apps.each do |app|
35
39
  @app = app
40
+ @app_type = RubyYacht.configuration.find_app_type(@app.app_type)
36
41
  build_image 'app', "#{@project.system_prefix}-#{app.name}"
37
42
  end
43
+ @app = nil
38
44
 
39
45
  if @project.create_deploy_container
40
46
  build_image 'deploy'
@@ -55,6 +61,10 @@ module RubyYacht::Runner
55
61
  # current context, and write it to the Dockerfile in the tmp folder. It will
56
62
  # then build the image, and clear out the tmp folder.
57
63
  #
64
+ # Depending on the image type, it may also copy and interpret other template
65
+ # files. For instance, for app containers we also have erb files for the
66
+ # `startup.bash` and `before_startup.bash` scripts.
67
+ #
58
68
  # ### Parameters
59
69
  #
60
70
  # * **folder_name: String** The name of the folder containing the
@@ -65,12 +75,46 @@ module RubyYacht::Runner
65
75
  # dash, followed by the folder name.
66
76
  def build_image(folder_name, image_name = nil)
67
77
  Dir[File.join(File.dirname(File.dirname(__FILE__)), 'images', folder_name, '*')].each do |path|
68
- FileUtils.cp(path, 'tmp')
78
+ FileUtils.cp(path, File.join('tmp', File.basename(path)))
69
79
  end
80
+
81
+ templates =
82
+ case folder_name
83
+ when 'app' then ['Dockerfile', 'before_startup.bash', 'startup.bash']
84
+ else ['Dockerfile']
85
+ end
86
+
87
+ hook_events =
88
+ case folder_name
89
+ when 'app' then [:startup, :build_checkout]
90
+ when 'app-dependencies' then [:install_libraries]
91
+ when 'database' then [:load_database_seeds]
92
+ else []
93
+ end
94
+
95
+ app_types =
96
+ case folder_name
97
+ when 'database' then @project.apps.map(&:app_type).uniq
98
+ else [@app_type.name]
99
+ end
70
100
 
71
- File.open(File.join('tmp', 'Dockerfile'), 'w') do |file|
72
- contents = ERB.new(File.read(File.join('tmp', 'Dockerfile.erb'))).result(binding)
73
- file.write(contents)
101
+ hook_events.each do |event|
102
+ app_types.each do |app_type|
103
+ RubyYacht.configuration.fetch_hooks(app_type: app_type, event_type: event).each do |hook|
104
+ next unless hook.script_path
105
+ FileUtils.cp hook.script_path, "tmp"
106
+ end
107
+ end
108
+ end
109
+
110
+ templates.each do |template_name|
111
+ File.open(File.join('tmp', template_name), 'w') do |file|
112
+ template_contents = File.read(File.join('tmp', template_name + '.erb'))
113
+ file_contents = ERB.new(template_contents, nil, '<>').result(binding)
114
+ file_contents.gsub!(/\n\n\n+/, "\n\n")
115
+ file_contents.gsub!(/\n*\Z/, "")
116
+ file.write(file_contents)
117
+ end
74
118
  end
75
119
 
76
120
  image_name ||= "#{@project.system_prefix}-#{folder_name}"
@@ -78,5 +122,28 @@ module RubyYacht::Runner
78
122
 
79
123
  FileUtils.rm(Dir[File.join("tmp", "*")])
80
124
  end
125
+
126
+ # This method includes the hooks for an event in a Dockerfile.
127
+ #
128
+ # It will run the commands for the before hooks, then the during hooks, then
129
+ # the after hooks.
130
+ #
131
+ # ### Parameters
132
+ #
133
+ # * **event_type: Symbol** The event whose hooks we are running.
134
+ #
135
+ # ### Returns
136
+ #
137
+ # A String with the contents of the Dockerfile for running the hooks.
138
+ def include_event(event_type)
139
+ result = ""
140
+ [:before, :during, :after].each do |time|
141
+ before_hooks = RubyYacht.configuration.fetch_hooks(app_type: @app_type.name, event_type: event_type, event_time: time).each do |hook|
142
+ result += "RUN #{hook.command}\n"
143
+ end
144
+ end
145
+
146
+ result
147
+ end
81
148
  end
82
149
  end
@@ -59,7 +59,7 @@ module RubyYacht::Runner
59
59
  container_name = "#{project.system_prefix}-#{app}"
60
60
 
61
61
  docker "exec #{container_name} bash -c 'cd /var/code; git fetch; git checkout .; git checkout #{branch}; git pull'"
62
- docker "exec #{container_name} bash -c '/var/docker/update_database_config.rb; rake db:migrate'"
62
+ docker "exec #{container_name} /var/docker/before_startup.bash"
63
63
  docker "restart #{container_name}"
64
64
 
65
65
  true
data/lib/ruby_yacht.rb CHANGED
@@ -3,4 +3,5 @@ module RubyYacht
3
3
  end
4
4
 
5
5
  require 'ruby_yacht/dsl'
6
- require 'ruby_yacht/runner'
6
+ require 'ruby_yacht/runner'
7
+ require 'ruby_yacht/plugins'
data/ruby_yacht.gemspec CHANGED
@@ -1,7 +1,8 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'ruby_yacht'
3
- spec.version = '0.1.1'
3
+ spec.version = '0.2.0'
4
4
  spec.date = '2016-04-17'
5
+ spec.description = "A DSL for building docker containers for a family of Rails apps"
5
6
  spec.summary = "A DSL for building docker containers for a family of Rails apps"
6
7
  spec.authors = ["John Brownlee"]
7
8
  spec.email = 'apps@mail.johnbrownlee.com'
@@ -2,4 +2,4 @@ FROM ruby:2.3
2
2
 
3
3
  VOLUME /var/code
4
4
  WORKDIR /var/code
5
- CMD bash
5
+ CMD bundle install; bash
data/spec/dsl/app_spec.rb CHANGED
@@ -5,28 +5,62 @@ RSpec.describe RubyYacht::App do
5
5
  let(:name) { :test_app }
6
6
  let(:app) { RubyYacht::App::DSL.new(name).run(@builder).create_object }
7
7
 
8
+ before do
9
+ RubyYacht.configuration.clear
10
+ RubyYacht.configure do
11
+ app_type :test do
12
+ baseline_image 'ubuntu'
13
+ end
14
+ end
15
+ end
16
+
8
17
  it "creates an app with all fields" do
9
18
  @builder = Proc.new do
10
19
  repository_name 'brownleej/test-app'
20
+ app_type :test
11
21
  port 3000
12
22
  end
13
23
  expect(app.name).to eq :test_app
14
24
  expect(app.repository_name).to eq 'brownleej/test-app'
25
+ expect(app.app_type).to eq :test
15
26
  expect(app.port).to eq 3000
16
27
  end
17
28
 
18
29
  it "requires the repository name" do
19
30
  @builder = Proc.new do
20
31
  port 3000
32
+ app_type :test
21
33
  end
22
34
  expect do
23
35
  self.app
24
36
  end.to raise_exception("Missing required attribute repository_name for RubyYacht::App::DSL")
25
37
  end
26
38
 
39
+ it "requires the app type" do
40
+ @builder = Proc.new do
41
+ repository_name 'brownleej/test-app'
42
+ port 3000
43
+ end
44
+ expect do
45
+ self.app
46
+ end.to raise_exception("Missing required attribute app_type for RubyYacht::App::DSL")
47
+ end
48
+
49
+ it "requires a defined app type" do
50
+ @builder = Proc.new do
51
+ app_type :invalid
52
+ repository_name 'brownleej/test-app'
53
+ port 3000
54
+ end
55
+ expect do
56
+ self.app
57
+ end.to raise_exception("App has invalid app type `invalid`")
58
+ end
59
+
27
60
  it "defaults the port to 8080" do
28
61
  @builder = Proc.new do
29
62
  repository_name 'brownleej/test-app'
63
+ app_type :test
30
64
  end
31
65
  expect(app.port).to eq 8080
32
66
  end