orchestration 0.2.8 → 0.3.0

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: 776a11504e5b33803ff0375a1d168b08567b8e97
4
- data.tar.gz: 52a848059b2fb338222b72cc00590f6d3760ae13
3
+ metadata.gz: 13a569a8391d7110efc2606ba1d486e29e342080
4
+ data.tar.gz: c5eed32f638f89dce179cd93c8f634504fe628d2
5
5
  SHA512:
6
- metadata.gz: a549fe23607afe2b5b0e46e28956a64f0dde1f43fcd900b6458cad298a0f05073c6aa1899908aaf8e978de940beb19414d3374471b2edfe8a4c1729ae50b97b0
7
- data.tar.gz: be3f1f287527ae67f1ca236aba5d1e2acd68cf34136916943c6a284e634330b1bea89c81aabcc6b3b5936c90e882f4e33fbb4cbd18cfc80d998d2033f03ea1de
6
+ metadata.gz: 91481f33e8f48bfabe18ab3c36fab7381060c631a62da15f3806197f7ec3ef08815a93790d132cf8431606eb6d110792f86c26fe3b7783a2e347a61f6e05a0cc
7
+ data.tar.gz: 32f5222a028460e9d5bb5b4477dae935a83692d7f50dae1e62d0b24b19fb3fb4db5c38453f2a1f9a787502868bb45d0a0284d9a99d331507c7cef9278ef7f7ac
data/.gitignore CHANGED
@@ -19,8 +19,7 @@ spec/dummy/log/*
19
19
  !spec/dummy/log/.keep
20
20
  spec/dummy/.gitignore
21
21
  spec/dummy/Makefile
22
- spec/dummy/docker/*
23
- spec/dummy/docker-compose.yml
22
+ spec/dummy/orchestration/*
24
23
  spec/dummy/config/unicorn.rb
25
24
 
26
25
  orchestration-*.gem
@@ -0,0 +1,2 @@
1
+ ignore:
2
+ - i18n
data/Makefile CHANGED
@@ -3,3 +3,4 @@
3
3
  test:
4
4
  ./bin/rspec
5
5
  ./bin/rubocop
6
+ ./bin/strong_versions
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.2.8'
21
+ gem 'orchestration', '~> 0.3.0'
22
22
  ```
23
23
 
24
24
  And then build your bundle:
@@ -32,11 +32,14 @@ $ bundle install
32
32
 
33
33
  A _Rake_ task is provided to generate the following files:
34
34
 
35
- * `Makefile` - provides easy access to all _Orchestration_ utilities.
36
35
  * `.gitignore` - ensures any unwanted files created by _Orchestration_ do not clutter your project's version control system.
37
- * `docker/Dockerfile` - a ready-to-use _Docker_ build script which should need minimal (if any) modification to build your _Rails_ project.
38
- * `docker-compose.yml` - a custom-made set of services to allow you to run your application's dependencies locally.
39
36
  * `.orchestration.yml` - _Orchestration_ internal configuration, e.g. _Docker_ username.
37
+ * `Makefile` - Adds `orchestration/Makefile` as an `include` to avoid clobbering any existing _make_ commands.
38
+ * `orchestration/docker-compose.yml` - a custom-made set of services to allow you to run your application's dependencies locally.
39
+ * `orchestration/Dockerfile` - a ready-to-use _Docker_ build script which should need minimal (if any) modification to build your _Rails_ project.
40
+ * `orchestration/entrypoint.sh` - Container setup for your Docker application.
41
+ * `orchestration/Makefile` - provides easy access to all _Orchestration_ utilities.
42
+ * `orchestration/yaml.bash` - A _bash_ _YAML_ parser (used by _make_ utilities).
40
43
 
41
44
  ### Building and pushing your project as a _Docker_ image
42
45
 
@@ -47,7 +50,7 @@ If your project has any dependencies on private _Git_ repositories then you wil
47
50
  * [GitHub](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/)
48
51
  * [Bitbucket](https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html)
49
52
 
50
- Create a file named `.env` in the same directory as your `docker-compose.yml` and add one or both of the following (note that _Bitbucket_ and _GitHub_ use a different format):
53
+ Create a file named `.env` in your project's root directory and add one or both of the following (note that _Bitbucket_ and _GitHub_ use a different format):
51
54
 
52
55
  ```bash
53
56
  BUNDLE_BITBUCKET__ORG=<bitbucket-username>:<app-password>
data/TODO CHANGED
@@ -1,15 +1,19 @@
1
- Be aware of yarn and include build steps in Dockerfile if present. Use dashboard
2
- front end as a reference.
3
-
4
1
  Provide volumes for databases and mount appropriate directories for adapter
5
2
 
6
3
  Refactor docker-compose services - these really belong in
7
4
  lib/orchestration/services/<service-name>/docker_compose.rb
8
5
 
9
- Add a note to README to handle `RAILS_LOG_TO_STDOUT` in development -
10
- unfortunately Rails ignores this value in development mode. (It's configured in
11
- config/environments/production.rb)
12
-
13
6
  Standardise on log formats - by policy or recommendation ?
14
7
 
15
8
  Redis support
9
+
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.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'strong_versions' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("strong_versions", "strong_versions")
@@ -1,6 +1,7 @@
1
1
  en:
2
2
  orchestration:
3
3
  attempt_limit: "Unable to reconnect after %{limit} attempts. Aborting."
4
+ default: "default"
4
5
 
5
6
  application:
6
7
  waiting: "Waiting for application: %{config}"
@@ -27,8 +28,15 @@ en:
27
28
  waiting: "Waiting for [%{service}]: %{config}"
28
29
  ready: "[%{service}] is ready."
29
30
 
30
- docker:
31
- username_request: "Enter your Docker registry username (used for tagging images)"
31
+ settings:
32
+ docker:
33
+ username:
34
+ description: "Docker registry username"
35
+ prompt: "username"
36
+
37
+ repository:
38
+ description: "Project name (will be used as Docker registry repository)"
39
+ prompt: "project name"
32
40
 
33
41
  rake:
34
42
  docker:
@@ -9,22 +9,17 @@ module Orchestration
9
9
 
10
10
  def definition
11
11
  {
12
- 'image' => image,
13
- 'entrypoint' => '/entrypoint.sh',
14
- 'command' => %w[
15
- bundle exec unicorn -c /application/config/unicorn.rb
16
- ],
12
+ 'image' => '${DOCKER_USERNAME}/${DOCKER_REPOSITORY}',
17
13
  'environment' => environment,
18
- 'expose' => [8080]
14
+ 'expose' => [8080],
15
+ 'volumes' => [
16
+ "#{@config.env.public_volume}:/app/public/:ro"
17
+ ]
19
18
  }
20
19
  end
21
20
 
22
21
  private
23
22
 
24
- def image
25
- "#{@config.docker_username}/#{@config.application_name}"
26
- end
27
-
28
23
  def environment
29
24
  {
30
25
  'DATABASE_URL' => @config.database_url,
@@ -9,9 +9,12 @@ module Orchestration
9
9
 
10
10
  def definition
11
11
  {
12
- 'image' => 'jwilder/nginx-proxy',
12
+ 'image' => 'rubyorchestration/nginx-proxy',
13
13
  'ports' => %w[3000:80],
14
- 'volumes' => ['/var/run/docker.sock:/tmp/docker.sock:ro']
14
+ 'volumes' => [
15
+ '/var/run/docker.sock:/tmp/docker.sock:ro',
16
+ "#{@config.env.public_volume}:/var/www/public/:ro"
17
+ ]
15
18
  }
16
19
  end
17
20
  end
@@ -3,7 +3,8 @@
3
3
  module Orchestration
4
4
  module DockerCompose
5
5
  class Services
6
- def initialize(options = {})
6
+ def initialize(env, options = {})
7
+ @env = env
7
8
  @configurations = {
8
9
  'application' => options.fetch(:application, nil),
9
10
  'database' => options.fetch(:database, nil),
@@ -14,7 +15,13 @@ module Orchestration
14
15
  end
15
16
 
16
17
  def structure
17
- { 'version' => '3.7', 'services' => services }
18
+ {
19
+ 'version' => '3.7',
20
+ 'services' => services,
21
+ 'volumes' => {
22
+ @env.public_volume => nil
23
+ }
24
+ }
18
25
  end
19
26
 
20
27
  def services
@@ -33,7 +33,7 @@ module Orchestration
33
33
  end
34
34
 
35
35
  def docker_compose_configuration_path
36
- root.join('docker-compose.yml')
36
+ orchestration_root.join('docker-compose.yml')
37
37
  end
38
38
 
39
39
  def docker_compose_config
@@ -44,10 +44,14 @@ module Orchestration
44
44
  docker_compose_configuration_path.file?
45
45
  end
46
46
 
47
- def application_name
47
+ def default_application_name
48
48
  Rails.application.class.parent.name.underscore
49
49
  end
50
50
 
51
+ def application_name
52
+ settings.get('docker.repository')
53
+ end
54
+
51
55
  def settings
52
56
  Settings.new(orchestration_configuration_path)
53
57
  end
@@ -57,5 +61,17 @@ module Orchestration
57
61
 
58
62
  Pathname.new(Dir.pwd)
59
63
  end
64
+
65
+ def orchestration_root
66
+ root.join(orchestration_dir_name)
67
+ end
68
+
69
+ def orchestration_dir_name
70
+ 'orchestration'
71
+ end
72
+
73
+ def public_volume
74
+ "#{application_name}_public"
75
+ end
60
76
  end
61
77
  end
@@ -4,45 +4,56 @@ module Orchestration
4
4
  module FileHelpers
5
5
  private
6
6
 
7
+ def orchestration_dir
8
+ path = @env.orchestration_root
9
+ FileUtils.mkdir(path) unless Dir.exist?(path)
10
+
11
+ path
12
+ end
13
+
7
14
  def template(template_name, context = {})
8
15
  Erubis::Eruby.new(read_template(template_name))
9
16
  .result(context)
10
17
  end
11
18
 
12
- def delete_and_inject_after(path, pattern, replacement)
13
- return write_file(path, pattern + replacement) unless File.exist?(path)
14
-
15
- input = File.read(path)
16
- index = append_index(pattern, input)
17
- output = input[0...index] + pattern + replacement
18
-
19
- return @terminal.write(:identical, relative_path(path)) if input == output
20
-
21
- update_file(path, output)
22
- end
23
-
24
- def append_index(pattern, input)
25
- return 0 if input.empty?
19
+ def inject_if_missing(path, content, index = 0)
20
+ lines = File.exist?(path) ? File.readlines(path).map(&:chomp) : []
21
+ if lines.any? { |line| line == content }
22
+ return @terminal.write(:skip, relative_path(path))
23
+ end
26
24
 
27
- index = input.index(pattern)
28
- index.nil? ? (input.size + 1) : index
25
+ lines.insert(index, content)
26
+ update_file(path, lines.join("\n"))
29
27
  end
30
28
 
31
29
  def relative_path(path)
32
30
  path.relative_path_from(Rails.root).to_s
33
31
  end
34
32
 
33
+ def simple_copy(template_name, dest)
34
+ update_file(dest, template(template_name))
35
+ end
36
+
35
37
  def write_file(path, content, options = {})
36
38
  relpath = relative_path(path)
37
39
  overwrite = options.fetch(:overwrite, true)
38
- return @terminal.write(:skip, relpath) if File.exist?(path) && !overwrite
40
+ present = File.exist?(path)
41
+ return @terminal.write(:skip, relpath) if present && !overwrite
39
42
 
40
43
  File.write(path, content)
41
44
  @terminal.write(:create, relative_path(path))
42
45
  end
43
46
 
44
47
  def update_file(path, content)
48
+ present = File.exist?(path)
49
+ return write_file(path, content) unless present
50
+
51
+ previous_content = File.read(path) if present
45
52
  File.write(path, content)
53
+ if present && previous_content == content
54
+ return @terminal.write(:skip, relative_path(path))
55
+ end
56
+
46
57
  @terminal.write(:update, relative_path(path))
47
58
  end
48
59
 
@@ -11,63 +11,61 @@ module Orchestration
11
11
  super
12
12
  @env = Environment.new
13
13
  @terminal ||= Terminal.new
14
+ @settings = Settings.new(@env.orchestration_configuration_path)
14
15
  end
15
16
 
16
17
  def orchestration_configuration
17
18
  path = @env.orchestration_configuration_path
18
- settings = Settings.new(path)
19
- docker_username(settings)
19
+ ask_setting('docker.username')
20
+ ask_setting('docker.repository', @env.default_application_name)
20
21
  relpath = relative_path(path)
21
- return @terminal.write(:create, relpath) unless settings.exist?
22
- return @terminal.write(:update, relpath) if settings.dirty?
22
+ return @terminal.write(:create, relpath) unless @settings.exist?
23
+ return @terminal.write(:update, relpath) if @settings.dirty?
23
24
 
24
25
  @terminal.write(:skip, relpath)
25
26
  end
26
27
 
27
28
  def makefile
28
- environment = {
29
- app_id: @env.application_name,
30
- wait_commands: wait_commands
31
- }
29
+ environment = { env: @env, wait_commands: wait_commands }
32
30
  content = template('Makefile', environment)
33
- path = @env.root.join('Makefile')
34
- delete_and_inject_after(path, "\n#!!orchestration\n", content)
31
+ path = @env.orchestration_root.join('Makefile')
32
+ path.exist? ? update_file(path, content) : write_file(path, content)
33
+ inject_if_missing(
34
+ @env.root.join('Makefile'),
35
+ 'include orchestration/Makefile'
36
+ )
35
37
  end
36
38
 
37
39
  def dockerfile
38
40
  content = template('Dockerfile', ruby_version: RUBY_VERSION)
39
- write_file(docker_dir.join('Dockerfile'), content, overwrite: false)
41
+ write_file(
42
+ orchestration_dir.join('Dockerfile'),
43
+ content,
44
+ overwrite: false
45
+ )
40
46
  end
41
47
 
42
48
  def entrypoint
43
49
  content = template('entrypoint.sh')
44
- path = docker_dir.join('entrypoint.sh')
50
+ path = orchestration_dir.join('entrypoint.sh')
45
51
  write_file(path, content, overwrite: false)
46
52
  FileUtils.chmod('a+x', path)
47
53
  end
48
54
 
49
55
  def gitignore
50
56
  path = @env.root.join('.gitignore')
51
- entries = [
52
- 'docker/.build/',
53
- 'docker/Gemfile',
54
- 'docker/Gemfile.lock',
55
- 'docker/*.gemspec'
56
- ]
57
+ entries = %w[.build/ Gemfile Gemfile.lock *.gemspec].map do |entry|
58
+ "#{@env.orchestration_dir_name}/#{entry}"
59
+ end
60
+
57
61
  ensure_lines_in_file(path, entries)
58
62
  end
59
63
 
60
64
  def docker_compose
61
- path = @env.root.join('docker-compose.yml')
65
+ path = @env.orchestration_root.join('docker-compose.yml')
62
66
  return if File.exist?(path)
63
67
 
64
- docker_compose = DockerCompose::Services.new(
65
- application: configuration(:application),
66
- database: configuration(:database),
67
- mongo: configuration(:mongo),
68
- rabbitmq: configuration(:rabbitmq),
69
- nginx_proxy: configuration(:nginx_proxy)
70
- )
68
+ docker_compose = DockerCompose::Services.new(@env, service_configurations)
71
69
  write_file(path, docker_compose.structure.to_yaml)
72
70
  end
73
71
 
@@ -77,8 +75,24 @@ module Orchestration
77
75
  write_file(path, content, overwrite: false)
78
76
  end
79
77
 
78
+ def yaml_bash
79
+ simple_copy('yaml.bash', @env.orchestration_root.join('yaml.bash'))
80
+ end
81
+
80
82
  private
81
83
 
84
+ def t(key)
85
+ I18n.t("orchestration.#{key}")
86
+ end
87
+
88
+ def service_configurations
89
+ Hash[
90
+ %i[application database mongo rabbitmq nginx_proxy].map do |key|
91
+ [key, configuration(key)]
92
+ end
93
+ ]
94
+ end
95
+
82
96
  def configuration(service)
83
97
  {
84
98
  application: Services::Application::Configuration,
@@ -99,18 +113,12 @@ module Orchestration
99
113
  ].compact.join(' ')
100
114
  end
101
115
 
102
- def docker_username(settings)
103
- return unless settings.get('docker.username').nil?
104
-
105
- @terminal.write(:setup, I18n.t('orchestration.docker.username_request'))
106
- settings.set('docker.username', @terminal.read('[username]:'))
107
- end
108
-
109
- def docker_dir
110
- path = @env.root.join('docker')
111
- FileUtils.mkdir(path) unless Dir.exist?(path)
116
+ def ask_setting(setting, default = nil)
117
+ return unless @settings.get(setting).nil?
112
118
 
113
- path
119
+ @terminal.write(:setup, t("settings.#{setting}.description"))
120
+ prompt = t("settings.#{setting}.prompt")
121
+ @settings.set(setting, @terminal.read(prompt, default))
114
122
  end
115
123
  end
116
124
  end
@@ -18,7 +18,7 @@ module Orchestration
18
18
  end
19
19
 
20
20
  def application_name
21
- @env.application_name
21
+ @env.settings.get('docker.repository')
22
22
  end
23
23
 
24
24
  def friendly_config
@@ -3,7 +3,7 @@
3
3
  module Orchestration
4
4
  module Services
5
5
  module ConfigurationBase
6
- attr_reader :settings, :service_name
6
+ attr_reader :settings, :service_name, :env
7
7
 
8
8
  def self.included(base)
9
9
  base.extend(ClassMethods)
@@ -5,9 +5,20 @@ RUN apt-get update \
5
5
  && apt-get install -y node.js gosu sendmail \
6
6
  && rm -rf /var/lib/apt/lists/* \
7
7
  && gem install bundler \
8
- && mkdir /application
9
- WORKDIR /application
10
- COPY Gemfile Gemfile.lock ./
11
- RUN bundle install --deployment
8
+ && mkdir /app<%if defined?(Webpacker) %> \
9
+ && curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash \
10
+ && . /root/.bashrc \
11
+ && nvm install 10.11.0 \
12
+ && npm install -g yarn<% end %>
13
+ WORKDIR /app
14
+ COPY .build/Gemfile .build/Gemfile.lock ./
15
+ RUN bundle install --without development test --deployment
16
+ <% if defined?(Webpacker) %>
17
+ COPY .build/package.json .build/yarn.lock ./
18
+ RUN . /root/.bashrc && yarn install
19
+ <% end %>
12
20
  COPY entrypoint.sh /
13
21
  ADD .build/context.tar.gz .
22
+ <% if defined?(Webpacker) %>RUN . /root/.bashrc && yarn install && bundle exec rake assets:precompile<% else %>RUN bundle exec rake assets:precompile<% end %>
23
+ ENTRYPOINT ["/entrypoint.sh"]
24
+ CMD ["bundle", "exec", "unicorn", "-c", "/app/config/unicorn.rb"]
@@ -1,20 +1,10 @@
1
-
2
- # Do not edit this file below this point. Any changes will be overwritten.
3
- #
4
- # Example `test` command which will start and wait for all services before
5
- # running tests:
6
- #
7
- # test: start wait
8
- # bundle exec rspec
9
- # yarn test app/javascript
10
- # bundle exec rubocop
11
- # yarn run eslint app/javascript
12
- #
13
- .PHONY: start stop migrate docker build push start <%= wait_commands %>
1
+ .PHONY: start stop migrate docker build push start logs compose config <%= wait_commands %>
14
2
 
15
3
  ### Container management commands ###
16
4
 
17
- COMPOSE:=HOST_UID=$(shell id -u) docker-compose
5
+ DOCKER_USERNAME:=$(shell bash ./<%= env.orchestration_dir_name %>/yaml.bash docker_username)
6
+ DOCKER_REPOSITORY:=$(shell bash ./<%= env.orchestration_dir_name %>/yaml.bash docker_repository)
7
+ COMPOSE:=HOST_UID=$(shell id -u) DOCKER_USERNAME=${DOCKER_USERNAME} DOCKER_REPOSITORY=${DOCKER_REPOSITORY} docker-compose -p $(shell bash ./<%= env.orchestration_dir_name %>/yaml.bash docker_repository) -f orchestration/docker-compose.yml
18
8
 
19
9
  start:
20
10
  @echo "Starting containers..."
@@ -32,6 +22,15 @@ start-application:
32
22
  @make wait-nginx-proxy wait-application
33
23
  @echo "Application started."
34
24
 
25
+ logs:
26
+ @${COMPOSE} logs
27
+
28
+ config:
29
+ @${COMPOSE} config
30
+
31
+ compose:
32
+ @${COMPOSE} $$cmd
33
+
35
34
  ### Database utility commands ###
36
35
 
37
36
  migrate: wait-database
@@ -67,19 +66,21 @@ GIT_BRANCH:=$(if $(BRANCH),$(BRANCH),$(shell git rev-parse --abbrev-ref HEAD))
67
66
 
68
67
  build:
69
68
  @echo "Preparing build from ${GIT_BRANCH}"
70
- @mkdir -p ./docker/.build
71
- @git show ${GIT_BRANCH}:./Gemfile > ./docker/Gemfile
72
- @git show ${GIT_BRANCH}:./Gemfile.lock > ./docker/Gemfile.lock
69
+ @mkdir -p ./<%= env.orchestration_dir_name %>/.build
70
+ @git show ${GIT_BRANCH}:./Gemfile > ./<%= env.orchestration_dir_name %>/.build/Gemfile
71
+ @git show ${GIT_BRANCH}:./Gemfile.lock > ./<%= env.orchestration_dir_name %>/.build/Gemfile.lock
72
+ <% if defined?(Webpacker) %>@git show ${GIT_BRANCH}:./package.json > ./<%= env.orchestration_dir_name %>/.build/package.json<% end %>
73
+ <% if defined?(Webpacker) %>@git show ${GIT_BRANCH}:./yarn.lock > ./<%= env.orchestration_dir_name %>/.build/yarn.lock<% end %>
73
74
  @echo "Building..."
74
- @git archive --format tar.gz -o docker/.build/context.tar.gz ${GIT_BRANCH}
75
+ @git archive --format tar.gz -o ./<%= env.orchestration_dir_name %>/.build/context.tar.gz ${GIT_BRANCH}
75
76
  @docker build \
76
77
  --build-arg BUNDLE_GITHUB__COM \
77
78
  --build-arg BUNDLE_BITBUCKET__ORG \
78
- -t $(shell bin/rake orchestration:docker:username)/<%= app_id %> \
79
- -t $(shell bin/rake orchestration:docker:username)/<%= app_id %>:$(shell git rev-parse --short --verify ${GIT_BRANCH}) \
80
- ./docker/
79
+ -t ${DOCKER_USERNAME}/${DOCKER_REPOSITORY} \
80
+ -t ${DOCKER_USERNAME}/${DOCKER_REPOSITORY}:$(shell git rev-parse --short --verify ${GIT_BRANCH}) \
81
+ ./<%= env.orchestration_dir_name %>/
81
82
  @echo "Build complete."
82
83
 
83
84
  push: VERSION := $(shell git rev-parse --short --verify ${GIT_BRANCH})
84
85
  push:
85
- docker push $(shell bin/rake orchestration:docker:username)/<%= app_id %>:${VERSION}
86
+ docker push ${DOCKER_USERNAME}/${DOCKER_REPOSITORY}:${VERSION}
@@ -1,7 +1,9 @@
1
1
  #!/bin/sh
2
2
  set -u
3
3
  id owner >/dev/null 2>&1 || useradd -u ${HOST_UID} -m -o owner
4
- mkdir -p /application/tmp/pids
5
- chown -R owner:owner /application/tmp /application/log /application/db
6
- rm -f /application/tmp/pids/unicorn.pid
4
+ mkdir -p /app/tmp/pids
5
+ chown -R owner:owner /app/tmp /app/log /app/db
6
+ rm -f /app/tmp/pids/unicorn.pid
7
+ mkdir -p /var/www/public/
8
+ ln -nfs /app/public/* /var/www/public/
7
9
  exec gosu owner "$@"
@@ -0,0 +1,364 @@
1
+ {{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
2
+
3
+ {{ define "upstream" }}
4
+ {{ if .Address }}
5
+ {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}}
6
+ {{ if and .Container.Node.ID .Address.HostPort }}
7
+ # {{ .Container.Node.Name }}/{{ .Container.Name }}
8
+ server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
9
+ {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}}
10
+ {{ else if .Network }}
11
+ # {{ .Container.Name }}
12
+ server {{ .Network.IP }}:{{ .Address.Port }};
13
+ {{ end }}
14
+ {{ else if .Network }}
15
+ # {{ .Container.Name }}
16
+ {{ if .Network.IP }}
17
+ server {{ .Network.IP }} down;
18
+ {{ else }}
19
+ server 127.0.0.1 down;
20
+ {{ end }}
21
+ {{ end }}
22
+
23
+ {{ end }}
24
+
25
+ # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
26
+ # scheme used to connect to this server
27
+ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
28
+ default $http_x_forwarded_proto;
29
+ '' $scheme;
30
+ }
31
+
32
+ # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
33
+ # server port the client connected to
34
+ map $http_x_forwarded_port $proxy_x_forwarded_port {
35
+ default $http_x_forwarded_port;
36
+ '' $server_port;
37
+ }
38
+
39
+ # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
40
+ # Connection header that may have been passed to this server
41
+ map $http_upgrade $proxy_connection {
42
+ default upgrade;
43
+ '' close;
44
+ }
45
+
46
+ # Apply fix for very long server names
47
+ server_names_hash_bucket_size 128;
48
+
49
+ # Default dhparam
50
+ {{ if (exists "/etc/nginx/dhparam/dhparam.pem") }}
51
+ ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
52
+ {{ end }}
53
+
54
+ # Set appropriate X-Forwarded-Ssl header
55
+ map $scheme $proxy_x_forwarded_ssl {
56
+ default off;
57
+ https on;
58
+ }
59
+
60
+ gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
61
+
62
+ log_format vhost '$host $remote_addr - $remote_user [$time_local] '
63
+ '"$request" $status $body_bytes_sent '
64
+ '"$http_referer" "$http_user_agent"';
65
+
66
+ access_log off;
67
+
68
+ {{ if $.Env.RESOLVERS }}
69
+ resolver {{ $.Env.RESOLVERS }};
70
+ {{ end }}
71
+
72
+ {{ if (exists "/etc/nginx/proxy.conf") }}
73
+ include /etc/nginx/proxy.conf;
74
+ {{ else }}
75
+ # HTTP 1.1 support
76
+ proxy_http_version 1.1;
77
+ proxy_buffering off;
78
+ proxy_set_header Host $http_host;
79
+ proxy_set_header Upgrade $http_upgrade;
80
+ proxy_set_header Connection $proxy_connection;
81
+ proxy_set_header X-Real-IP $remote_addr;
82
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
83
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
84
+ proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
85
+ proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
86
+
87
+ # Mitigate httpoxy attack (see README for details)
88
+ proxy_set_header Proxy "";
89
+ {{ end }}
90
+
91
+ {{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }}
92
+ server {
93
+ server_name _; # This is just an invalid value which will never trigger on a real hostname.
94
+ listen 80;
95
+ {{ if $enable_ipv6 }}
96
+ listen [::]:80;
97
+ {{ end }}
98
+ access_log /var/log/nginx/access.log vhost;
99
+ return 503;
100
+ }
101
+
102
+ {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
103
+ server {
104
+ server_name _; # This is just an invalid value which will never trigger on a real hostname.
105
+ listen 443 ssl http2;
106
+ {{ if $enable_ipv6 }}
107
+ listen [::]:443 ssl http2;
108
+ {{ end }}
109
+ access_log /var/log/nginx/access.log vhost;
110
+ return 503;
111
+
112
+ ssl_session_tickets off;
113
+ ssl_certificate /etc/nginx/certs/default.crt;
114
+ ssl_certificate_key /etc/nginx/certs/default.key;
115
+ }
116
+ {{ end }}
117
+
118
+ {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}
119
+
120
+ {{ $host := trim $host }}
121
+ {{ $is_regexp := hasPrefix "~" $host }}
122
+ {{ $upstream_name := when $is_regexp (sha1 $host) $host }}
123
+
124
+ # {{ $host }}
125
+ upstream {{ $upstream_name }} {
126
+
127
+ {{ range $container := $containers }}
128
+ {{ $addrLen := len $container.Addresses }}
129
+
130
+ {{ range $knownNetwork := $CurrentContainer.Networks }}
131
+ {{ range $containerNetwork := $container.Networks }}
132
+ {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
133
+ ## Can be connected with "{{ $containerNetwork.Name }}" network
134
+
135
+ {{/* If only 1 port exposed, use that */}}
136
+ {{ if eq $addrLen 1 }}
137
+ {{ $address := index $container.Addresses 0 }}
138
+ {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
139
+ {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
140
+ {{ else }}
141
+ {{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
142
+ {{ $address := where $container.Addresses "Port" $port | first }}
143
+ {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
144
+ {{ end }}
145
+ {{ else }}
146
+ # Cannot connect to network of this container
147
+ server 127.0.0.1 down;
148
+ {{ end }}
149
+ {{ end }}
150
+ {{ end }}
151
+ {{ end }}
152
+ }
153
+
154
+ {{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
155
+ {{ $default_server := index (dict $host "" $default_host "default_server") $host }}
156
+
157
+ {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
158
+ {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
159
+
160
+ {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
161
+ {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
162
+
163
+ {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
164
+ {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }}
165
+
166
+ {{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to "Mozilla-Intermediate" */}}
167
+ {{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "Mozilla-Intermediate" }}
168
+
169
+ {{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}}
170
+ {{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }}
171
+
172
+ {{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
173
+ {{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
174
+
175
+
176
+ {{/* Get the first cert name defined by containers w/ the same vhost */}}
177
+ {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
178
+
179
+ {{/* Get the best matching cert by name for the vhost. */}}
180
+ {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}}
181
+
182
+ {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}}
183
+ {{ $vhostCert := trimSuffix ".crt" $vhostCert }}
184
+ {{ $vhostCert := trimSuffix ".key" $vhostCert }}
185
+
186
+ {{/* Use the cert specified on the container or fallback to the best vhost match */}}
187
+ {{ $cert := (coalesce $certName $vhostCert) }}
188
+
189
+ {{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
190
+
191
+ {{ if $is_https }}
192
+
193
+ {{ if eq $https_method "redirect" }}
194
+ server {
195
+ server_name {{ $host }};
196
+ listen 80 {{ $default_server }};
197
+ {{ if $enable_ipv6 }}
198
+ listen [::]:80 {{ $default_server }};
199
+ {{ end }}
200
+ access_log /var/log/nginx/access.log vhost;
201
+ return 301 https://$host$request_uri;
202
+ }
203
+ {{ end }}
204
+
205
+ server {
206
+ server_name {{ $host }};
207
+ listen 443 ssl http2 {{ $default_server }};
208
+ {{ if $enable_ipv6 }}
209
+ listen [::]:443 ssl http2 {{ $default_server }};
210
+ {{ end }}
211
+ access_log /var/log/nginx/access.log vhost;
212
+
213
+ {{ if eq $network_tag "internal" }}
214
+ # Only allow traffic from internal clients
215
+ include /etc/nginx/network_internal.conf;
216
+ {{ end }}
217
+
218
+ {{ if eq $ssl_policy "Mozilla-Modern" }}
219
+ ssl_protocols TLSv1.2 TLSv1.3;
220
+ ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
221
+ {{ else if eq $ssl_policy "Mozilla-Intermediate" }}
222
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
223
+ ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:!DSS';
224
+ {{ else if eq $ssl_policy "Mozilla-Old" }}
225
+ ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
226
+ ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP';
227
+ {{ else if eq $ssl_policy "AWS-TLS-1-2-2017-01" }}
228
+ ssl_protocols TLSv1.2 TLSv1.3;
229
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256';
230
+ {{ else if eq $ssl_policy "AWS-TLS-1-1-2017-01" }}
231
+ ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
232
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
233
+ {{ else if eq $ssl_policy "AWS-2016-08" }}
234
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
235
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
236
+ {{ else if eq $ssl_policy "AWS-2015-05" }}
237
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
238
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA';
239
+ {{ else if eq $ssl_policy "AWS-2015-03" }}
240
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
241
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA';
242
+ {{ else if eq $ssl_policy "AWS-2015-02" }}
243
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
244
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA';
245
+ {{ end }}
246
+
247
+ ssl_prefer_server_ciphers on;
248
+ ssl_session_timeout 5m;
249
+ ssl_session_cache shared:SSL:50m;
250
+ ssl_session_tickets off;
251
+
252
+ ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }};
253
+ ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }};
254
+
255
+ {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
256
+ ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
257
+ {{ end }}
258
+
259
+ {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
260
+ ssl_stapling on;
261
+ ssl_stapling_verify on;
262
+ ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
263
+ {{ end }}
264
+
265
+ {{ if (and (ne $https_method "noredirect") (ne $hsts "off")) }}
266
+ add_header Strict-Transport-Security "{{ trim $hsts }}" always;
267
+ {{ end }}
268
+
269
+ {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
270
+ include {{ printf "/etc/nginx/vhost.d/%s" $host }};
271
+ {{ else if (exists "/etc/nginx/vhost.d/default") }}
272
+ include /etc/nginx/vhost.d/default;
273
+ {{ end }}
274
+
275
+ root /var/www/public;
276
+ try_files $uri @application;
277
+
278
+ location @application {
279
+ {{ if eq $proto "uwsgi" }}
280
+ include uwsgi_params;
281
+ uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
282
+ {{ else if eq $proto "fastcgi" }}
283
+ root {{ trim $vhost_root }};
284
+ include fastcgi.conf;
285
+ fastcgi_pass {{ trim $upstream_name }};
286
+ {{ else }}
287
+ proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
288
+ {{ end }}
289
+
290
+ {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
291
+ auth_basic "Restricted {{ $host }}";
292
+ auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
293
+ {{ end }}
294
+ {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
295
+ include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
296
+ {{ else if (exists "/etc/nginx/vhost.d/default_location") }}
297
+ include /etc/nginx/vhost.d/default_location;
298
+ {{ end }}
299
+ }
300
+ }
301
+
302
+ {{ end }}
303
+
304
+ {{ if or (not $is_https) (eq $https_method "noredirect") }}
305
+
306
+ server {
307
+ server_name {{ $host }};
308
+ listen 80 {{ $default_server }};
309
+ {{ if $enable_ipv6 }}
310
+ listen [::]:80 {{ $default_server }};
311
+ {{ end }}
312
+ access_log /var/log/nginx/access.log vhost;
313
+
314
+ {{ if eq $network_tag "internal" }}
315
+ # Only allow traffic from internal clients
316
+ include /etc/nginx/network_internal.conf;
317
+ {{ end }}
318
+
319
+ {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
320
+ include {{ printf "/etc/nginx/vhost.d/%s" $host }};
321
+ {{ else if (exists "/etc/nginx/vhost.d/default") }}
322
+ include /etc/nginx/vhost.d/default;
323
+ {{ end }}
324
+
325
+ root /var/www/public;
326
+ try_files $uri @application;
327
+
328
+ location @application {
329
+ {{ if eq $proto "uwsgi" }}
330
+ include uwsgi_params;
331
+ uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
332
+ {{ else if eq $proto "fastcgi" }}
333
+ root {{ trim $vhost_root }};
334
+ include fastcgi.conf;
335
+ fastcgi_pass {{ trim $upstream_name }};
336
+ {{ else }}
337
+ proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
338
+ {{ end }}
339
+
340
+ {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
341
+ include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
342
+ {{ else if (exists "/etc/nginx/vhost.d/default_location") }}
343
+ include /etc/nginx/vhost.d/default_location;
344
+ {{ end }}
345
+ }
346
+ }
347
+
348
+ {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
349
+ server {
350
+ server_name {{ $host }};
351
+ listen 443 ssl http2 {{ $default_server }};
352
+ {{ if $enable_ipv6 }}
353
+ listen [::]:443 ssl http2 {{ $default_server }};
354
+ {{ end }}
355
+ access_log /var/log/nginx/access.log vhost;
356
+ return 500;
357
+
358
+ ssl_certificate /etc/nginx/certs/default.crt;
359
+ ssl_certificate_key /etc/nginx/certs/default.key;
360
+ }
361
+ {{ end }}
362
+
363
+ {{ end }}
364
+ {{ end }}
@@ -1,6 +1,6 @@
1
1
  listen '0.0.0.0:8080', :tcp_nopush => true
2
2
 
3
- pid '/application/tmp/pids/unicorn.pid'
3
+ pid '/app/tmp/pids/unicorn.pid'
4
4
 
5
5
  preload_app ENV.fetch('UNICORN_PRELOAD_APP', '1') == '1'
6
6
 
@@ -0,0 +1,22 @@
1
+ # https://stackoverflow.com/a/21189044 - thanks.
2
+ function parse_yaml {
3
+ local prefix=$2
4
+ local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
5
+ sed -ne "s|^\($s\):|\1|" \
6
+ -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
7
+ -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
8
+ awk -F$fs '{
9
+ indent = length($1)/2;
10
+ vname[indent] = $2;
11
+ for (i in vname) {if (i > indent) {delete vname[i]}}
12
+ if (length($3) > 0) {
13
+ vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
14
+ printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
15
+ }
16
+ }'
17
+ }
18
+
19
+ eval $(parse_yaml ./.orchestration.yml)
20
+
21
+ set -u
22
+ echo -n "$(eval echo \$$1)"
@@ -8,7 +8,6 @@ module Orchestration
8
8
  ready: :green,
9
9
  create: :green,
10
10
  update: :yellow,
11
- identical: :blue,
12
11
  status: :blue,
13
12
  setup: :blue,
14
13
  input: :red,
@@ -21,13 +20,22 @@ module Orchestration
21
20
  STDOUT.print colorize(desc, output, color_name)
22
21
  end
23
22
 
24
- def read(message)
25
- write(:input, message + ' ', nil, false)
26
- STDIN.gets.chomp
23
+ def read(message, default = nil)
24
+ write(:input, prompt(message, default), nil, false)
25
+ result = STDIN.gets.chomp.strip
26
+ return default if result.empty?
27
+
28
+ result
27
29
  end
28
30
 
29
31
  private
30
32
 
33
+ def prompt(message, default)
34
+ return "(#{message}): " if default.nil?
35
+
36
+ "(#{message}) [#{I18n.t('orchestration.default')}: #{default}]: "
37
+ end
38
+
31
39
  def colorize(desc, message, color_name)
32
40
  color = if color_name.nil?
33
41
  COLOR_MAP.fetch(desc)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Orchestration
4
- VERSION = '0.2.8'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -42,5 +42,6 @@ Gem::Specification.new do |spec|
42
42
  spec.add_development_dependency 'rspec-its', '~> 1.2'
43
43
  spec.add_development_dependency 'rubocop', '~> 0.59.2'
44
44
  spec.add_development_dependency 'sqlite3', '~> 1.3'
45
+ spec.add_development_dependency 'strong_versions', '~> 0.3.1'
45
46
  spec.add_development_dependency 'webmock', '~> 3.4'
46
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orchestration
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-16 00:00:00.000000000 Z
11
+ date: 2018-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -248,6 +248,20 @@ dependencies:
248
248
  - - "~>"
249
249
  - !ruby/object:Gem::Version
250
250
  version: '1.3'
251
+ - !ruby/object:Gem::Dependency
252
+ name: strong_versions
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: 0.3.1
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: 0.3.1
251
265
  - !ruby/object:Gem::Dependency
252
266
  name: webmock
253
267
  requirement: !ruby/object:Gem::Requirement
@@ -270,12 +284,14 @@ executables:
270
284
  - rspec
271
285
  - rubocop
272
286
  - setup
287
+ - strong_versions
273
288
  extensions: []
274
289
  extra_rdoc_files: []
275
290
  files:
276
291
  - ".gitignore"
277
292
  - ".rspec"
278
293
  - ".rubocop.yml"
294
+ - ".strong_versions.yml"
279
295
  - ".travis.yml"
280
296
  - Gemfile
281
297
  - Makefile
@@ -286,6 +302,7 @@ files:
286
302
  - bin/rspec
287
303
  - bin/rubocop
288
304
  - bin/setup
305
+ - bin/strong_versions
289
306
  - config/locales/en.yml
290
307
  - lib/Rakefile
291
308
  - lib/orchestration.rb
@@ -331,7 +348,9 @@ files:
331
348
  - lib/orchestration/templates/Dockerfile.erb
332
349
  - lib/orchestration/templates/Makefile.erb
333
350
  - lib/orchestration/templates/entrypoint.sh.erb
351
+ - lib/orchestration/templates/nginx.tmpl.erb
334
352
  - lib/orchestration/templates/unicorn.rb.erb
353
+ - lib/orchestration/templates/yaml.bash.erb
335
354
  - lib/orchestration/terminal.rb
336
355
  - lib/orchestration/version.rb
337
356
  - lib/tasks/orchestration.rake