orchestration 0.5.2 → 0.5.7

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
  SHA256:
3
- metadata.gz: a8525fb5a3f6bd07564ec80a6f2663f1ab3157f7e4ad7e3db411844e36ae42d1
4
- data.tar.gz: 7828198b463b1e40a0f4b085efe848b742aa04dded4278df485706c358eed618
3
+ metadata.gz: a9dd4904167052dde3f1cbf5f441225f2719c42467204077d943534f73bb2873
4
+ data.tar.gz: 873bea476a74979aa917e87449c0da23a1c63128c1257a6c0ab40414e464a2ef
5
5
  SHA512:
6
- metadata.gz: 2ca10bebdc3fc34cda1491dfa694386b591c0f1122c1a2d4a1c16273a6f936cdf49aa7b97b9b91b1ab271ff41b0b881a9a736956ff5d3709c07ee42da96030ea
7
- data.tar.gz: 6c86b19c30c5265ed9ad37af22ffab8176c88e815918a62278b2cfe82d4ec9cb4f4237f2202e88423ea1580a112058231e5f5005a4e1c5cf63cf7809e105a3d3
6
+ metadata.gz: bf66b2842c17ed6cb43d7a02045f9c35f7a4bf6fe70e8c7d4425ddee8c72c24a810ab64175fa00430da2c38b3c9e66dde62cedc31b65a68b5b51ba65e53c87c9
7
+ data.tar.gz: 4e3aa0a3ba1f8b2f9b7bd58e6a553a86924cbf0805657de8b5b6c63eeefc8ee38bea13a560f093410778690c5c0d9228359e9d8a0f38bd6e600b1c42e80bd253
@@ -13,6 +13,7 @@ Metrics/ModuleLength:
13
13
  - 'lib/orchestration/file_helpers.rb'
14
14
 
15
15
  AllCops:
16
+ NewCops: enable
16
17
  Exclude:
17
18
  - 'bin/**/*'
18
19
  - 'db/schema.rb'
data/README.md CHANGED
@@ -1,13 +1,5 @@
1
1
  # Orchestration
2
2
 
3
- ```
4
- I've got two tickets to the game
5
- It'd be great if I could take you to it this Sunday
6
- --Nickelback
7
- ```
8
-
9
- ## Overview
10
-
11
3
  _Orchestration_ aims to provide a convenient and consistent process for working with _Rails_ and _Docker_ without obscuring underlying components.
12
4
 
13
5
  At its core _Orchestration_ is simply a `Makefile` and a set of `docker-compose.yml` files with sensible, general-purpose default settings. Users are encouraged to tailor the generated build-out to suit their application; once the build-out has been generated it belongs to the application.
@@ -35,7 +27,7 @@ The below screenshot demonstrates _Orchestration_ being installed in a brand new
35
27
  Add _Orchestration_ to your Gemfile:
36
28
 
37
29
  ```ruby
38
- gem 'orchestration', '~> 0.5.2'
30
+ gem 'orchestration', '~> 0.5.7'
39
31
  ```
40
32
 
41
33
  Install:
@@ -143,7 +135,20 @@ Note that `git archive` is used to generate the build context. Any uncommitted c
143
135
  make build
144
136
  ```
145
137
 
146
- See [build environment](#build-environment) for more details.
138
+ The `include` option can also be passed to provide a manifest file. Any files listed in this file will also be built into the _Docker_ image. Files **must** be located within the project directory.
139
+
140
+ ```bash
141
+ make build include=manifest.txt
142
+ ```
143
+
144
+ ```bash
145
+ # manifest.txt
146
+ doc/api/swagger.json
147
+ doc/api/soap.xml
148
+ doc/api/doc.html
149
+ ```
150
+
151
+ See also [build environment](#build-environment) if you use gems hosted on private _GitHub_/_Bitbucket_ repositories.
147
152
 
148
153
  #### Push latest image
149
154
 
@@ -222,6 +227,8 @@ To connect via _SSH_ to a remote swarm and deploy, pass the `manager` parameter:
222
227
  make deploy manager=user@manager.swarm.example.com
223
228
  ```
224
229
 
230
+ The file `orchestration/docker-compose.production.yml` is created automatically. If your `RAILS_ENV` is set to something other than `production` then another file will need to be created (e.g. `orchestration/docker-compose.staging.yml`). In most cases this file can be a _symlink_ to the original `production` configuration and environment variables can be used to customise the content.
231
+
225
232
  #### Roll back a deployment
226
233
 
227
234
  Roll back the `app` service of your stack:
@@ -265,7 +272,7 @@ Note that the following two variables _must_ be set in the relevant `.env` file
265
272
 
266
273
  ```
267
274
  # Published port for your application service:
268
- CONTAINER_PORT=3000
275
+ PUBLISH_PORT=3000
269
276
 
270
277
  # Number of replicas of your application service:
271
278
  REPLICAS=5
@@ -320,7 +327,7 @@ See related documentation:
320
327
  | `WEB_HEALTHCHECK_PATH` | Path expected to return a successful response | `/` |
321
328
  | `WEB_HEALTHCHECK_READ_TIMEOUT` | Number of seconds to wait for data before failing healthcheck | `10` |
322
329
  | `WEB_HEALTHCHECK_OPEN_TIMEOUT` | Number of seconds to wait for connection before failing healthcheck | `10` |
323
- | `WEB_HEALTHCHECK_SUCCESS_CODES` | Comma-separated list of HTTP status codes that will be deemed a success | `200,202,204` |
330
+ | `WEB_HEALTHCHECK_SUCCESS_CODES` | Comma-separated list of HTTP status codes that will be deemed a success | `200,201,202,204` |
324
331
 
325
332
  If your application does not have a suitable always-available route to use as a healthcheck, the following one-liner may be useful:
326
333
 
@@ -11,7 +11,7 @@ require 'paint'
11
11
  begin
12
12
  require 'rails'
13
13
  rescue LoadError
14
- warn('[orchestration] Rails not detected; skipping.')
14
+ warn('[orchestration] Running in non-Rails mode.')
15
15
  end
16
16
 
17
17
  I18n.load_path += Dir[File.join(File.expand_path('..', __dir__),
@@ -42,7 +42,7 @@ module Orchestration
42
42
 
43
43
  def self.error(key, options = {})
44
44
  warn('# Orchestration Error')
45
- warn('# ' + I18n.t("orchestration.#{key}", options))
45
+ warn("# #{I18n.t("orchestration.#{key}", options)}")
46
46
  end
47
47
 
48
48
  def self.random_local_port
@@ -64,7 +64,8 @@ module Orchestration
64
64
  'environment' => environment,
65
65
  'ports' => ports,
66
66
  'deploy' => deploy,
67
- 'logging' => logging
67
+ 'logging' => logging,
68
+ 'networks' => networks
68
69
  }
69
70
  end
70
71
 
@@ -75,10 +76,7 @@ module Orchestration
75
76
  end
76
77
 
77
78
  def deploy
78
- {
79
- 'mode' => 'replicated',
80
- 'replicas' => '${REPLICAS}'
81
- }
79
+ { 'mode' => 'replicated', 'replicas' => '${REPLICAS:-3}' }
82
80
  end
83
81
 
84
82
  def logging
@@ -91,29 +89,34 @@ module Orchestration
91
89
  }
92
90
  end
93
91
 
92
+ def networks
93
+ { 'local' => {} }
94
+ end
95
+
94
96
  def environment
95
97
  {
96
98
  'RAILS_LOG_TO_STDOUT' => '1',
97
99
  'RAILS_SERVE_STATIC_FILES' => '1',
98
100
  'WEB_PRELOAD_APP' => '1',
99
- 'WEB_HEALTHCHECK_PATH' => '/'
101
+ 'WEB_HEALTHCHECK_PATH' => '/',
102
+ 'DATABASE_URL' => database_url
100
103
  }.merge(Hash[inherited_environment.map { |key| [key, nil] }])
101
104
  end
102
105
 
106
+ def database_url
107
+ {
108
+ 'postgresql' => 'postgresql://postgres:password@database-local:5432/production',
109
+ 'mysql2' => 'mysql2://root:password@database-local:3306/production',
110
+ 'sqlite3' => 'sqlite3:db/production.sqlite3'
111
+ }.fetch(DockerCompose::ComposeConfiguration.database_adapter_name)
112
+ end
113
+
103
114
  def inherited_environment
104
- %w[
105
- DATABASE_URL
106
- HOST_UID
107
- RAILS_ENV
108
- SECRET_KEY_BASE
109
- WEB_CONCURRENCY
110
- WEB_TIMEOUT
111
- WEB_WORKER_PROCESSES
112
- ]
115
+ %w[HOST_UID RAILS_ENV SECRET_KEY_BASE WEB_CONCURRENCY WEB_TIMEOUT WEB_WORKER_PROCESSES]
113
116
  end
114
117
 
115
118
  def ports
116
- ['${CONTAINER_PORT:?CONTAINER_PORT must be provided}:8080']
119
+ ['${PUBLISH_PORT:?PUBLISH_PORT must be provided}:8080']
117
120
  end
118
121
  end
119
122
  end
@@ -21,6 +21,10 @@ module Orchestration
21
21
  {}.merge(database_volume).merge(mongo_volume)
22
22
  end
23
23
 
24
+ def networks
25
+ { 'local' => { 'name' => '${COMPOSE_PROJECT_NAME}' } }
26
+ end
27
+
24
28
  private
25
29
 
26
30
  def services_available
@@ -16,7 +16,8 @@ module Orchestration
16
16
 
17
17
  {
18
18
  'image' => adapter.image,
19
- 'environment' => adapter.environment
19
+ 'environment' => adapter.environment,
20
+ 'networks' => networks
20
21
  }.merge(ports).merge(volumes)
21
22
  end
22
23
 
@@ -32,6 +33,12 @@ module Orchestration
32
33
  adapter.default_port
33
34
  end
34
35
 
36
+ def networks
37
+ return {} unless @environment == :production
38
+
39
+ { 'local' => { 'aliases' => ['database-local'] } }
40
+ end
41
+
35
42
  def ports
36
43
  return {} unless %i[development test].include?(@environment)
37
44
 
@@ -41,7 +41,8 @@ module Orchestration
41
41
  {
42
42
  'version' => compose_config(environment).version,
43
43
  'services' => services(environment),
44
- 'volumes' => volumes(environment)
44
+ 'volumes' => volumes(environment),
45
+ 'networks' => networks(environment)
45
46
  }
46
47
  end
47
48
 
@@ -55,6 +56,12 @@ module Orchestration
55
56
  compose_config(environment).volumes
56
57
  end
57
58
 
59
+ def networks(environment)
60
+ return {} unless environment == :production
61
+
62
+ compose_config(environment).networks
63
+ end
64
+
58
65
  def compose_config(environment)
59
66
  DockerCompose::Configuration.new(
60
67
  @env,
@@ -35,12 +35,12 @@ module Orchestration
35
35
  client.open_timeout = ENV.fetch('WEB_HEALTHCHECK_OPEN_TIMEOUT', '10').to_i
36
36
 
37
37
  client.start do |request|
38
- request.get(ENV.fetch('WEB_HEALTHCHECK_PATH') { '/' })
38
+ request.get(ENV.fetch('WEB_HEALTHCHECK_PATH', '/'))
39
39
  end
40
40
  end
41
41
 
42
42
  def success_codes
43
- ENV.fetch('WEB_HEALTHCHECK_SUCCESS_CODES', '200,202,204').split(',')
43
+ ENV.fetch('WEB_HEALTHCHECK_SUCCESS_CODES', '200,201,202,204').split(',')
44
44
  end
45
45
 
46
46
  def success?(code)
@@ -82,7 +82,7 @@ module Orchestration
82
82
  end
83
83
 
84
84
  def app_port
85
- ENV.fetch('CONTAINER_PORT', ENV.fetch('WEB_PORT', '3000')).to_i
85
+ ENV.fetch('PUBLISH_PORT', ENV.fetch('WEB_PORT', '3000')).to_i
86
86
  end
87
87
 
88
88
  def app_name
@@ -18,9 +18,7 @@ module Orchestration
18
18
 
19
19
  def inject_if_missing(path, content, index = 0)
20
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
21
+ return @terminal.write(:skip, relative_path(path)) if lines.any? { |line| line == content }
24
22
 
25
23
  lines.insert(index, content)
26
24
  update_file(path, lines.join("\n"))
@@ -56,9 +54,7 @@ module Orchestration
56
54
  return create_file(path, content) unless present
57
55
 
58
56
  previous_content = File.read(path) if present
59
- if skip?(present, content, previous_content, options)
60
- return @terminal.write(:skip, relative_path(path))
61
- end
57
+ return @terminal.write(:skip, relative_path(path)) if skip?(present, content, previous_content, options)
62
58
 
63
59
  backup(path, previous_content) if options.fetch(:backup, false)
64
60
  File.write(path, content)
@@ -27,7 +27,7 @@ module Orchestration
27
27
  @terminal.write(:skip, relpath)
28
28
  end
29
29
 
30
- def verify_makefile(skip = true)
30
+ def verify_makefile(skip: true)
31
31
  # Only run when called explicitly [from Rake tasks].
32
32
  # (I know this is hacky).
33
33
  return if skip
@@ -127,10 +127,6 @@ module Orchestration
127
127
  ensure_lines_in_file(path, lines)
128
128
  end
129
129
 
130
- def deploy_mk
131
- simple_copy('deploy.mk')
132
- end
133
-
134
130
  private
135
131
 
136
132
  def t(key)
@@ -16,7 +16,7 @@ module Orchestration
16
16
  end
17
17
 
18
18
  def run
19
- return echo_missing unless @service.configuration.configured?
19
+ return unless @service.configuration.configured?
20
20
 
21
21
  echo_start
22
22
  success = attempt_connection
@@ -39,14 +39,6 @@ module Orchestration
39
39
  false
40
40
  end
41
41
 
42
- def echo_missing
43
- @terminal.write(
44
- @service_name.to_sym,
45
- "#{@service.configuration.error} (skipping)",
46
- :error
47
- )
48
- end
49
-
50
42
  def echo_start
51
43
  @terminal.write(@service_name.to_sym, '', :status)
52
44
  end
@@ -42,9 +42,7 @@ module Orchestration
42
42
 
43
43
  def url_config
44
44
  uri = URI.parse(@env.mongo_url)
45
- unless uri.scheme == 'mongodb'
46
- raise ArgumentError, 'MONGO_URL protocol must be mongodb://'
47
- end
45
+ raise ArgumentError, 'MONGO_URL protocol must be mongodb://' unless uri.scheme == 'mongodb'
48
46
 
49
47
  url_config_structure(uri)
50
48
  end
@@ -13,7 +13,9 @@ 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.11.0 \
16
+ && nvm install 10.13.0 \
17
+ && npm config set user 0 \
18
+ && npm config set unsafe-perm true \
17
19
  && npm install -g yarn<% end %>
18
20
  WORKDIR /app
19
21
  COPY .build/Gemfile .build/Gemfile.lock ./
@@ -15,8 +15,10 @@ test: test-setup
15
15
 
16
16
  # Start development containers and create/migrate/seed database
17
17
  .PHONY: develop
18
- develop: start
18
+ develop:
19
19
  bundle install
20
+ @$(MAKE) start env=test
21
+ @$(MAKE) start env=development
20
22
  bundle exec rake db:create
21
23
  bundle exec rake db:migrate
22
24
  bundle exec rake db:seed
@@ -1,7 +1,6 @@
1
- # Configure which port your `app` service will listen on:
2
- CONTAINER_PORT=3000
1
+ # Set any development environment variables here
2
+ # e.g.:
3
+ #
4
+ # DISABLE_SPRING=1
3
5
 
4
- # Use `make deploy` to deploy your application stack to a Docker Swarm.
5
- # Use this setting to control the number of replicas to create for your
6
- # application service:
7
- REPLICAS=1
6
+ PUBLISH_PORT=3000
@@ -31,6 +31,7 @@ stdout=${pwd}/log/orchestration.stdout.log
31
31
  stderr=${pwd}/log/orchestration.stderr.log
32
32
  log_path_length=$(shell echo "${stdout}" | wc -c)
33
33
  ifndef verbose
34
+ log_tee:= 2>&1 | tee -a ${stdout}
34
35
  log:= >>${stdout} 2>>${stderr}
35
36
  progress_point:=perl -e 'while( my $$line = <STDIN> ) { printf("."); select()->flush(); }'
36
37
  log_progress:= > >(tee -ai ${stdout} >&1 | ${progress_point}) 2> >(tee -ai ${stderr} 2>&1 | ${progress_point})
@@ -87,17 +88,19 @@ restore_env:=( \
87
88
 
88
89
  key_chars:=[a-zA-Z0-9_]
89
90
  censored:=**********
90
- censor=sed 's/\(^${key_chars}*$(1)${key_chars}*\)=\(.*\)$$/\1=${censored}/'
91
- censor_urls:=sed 's|\([a-zA-Z0-9_+]\+://.*:\).*\(@.*\)$$|\1${censored}\2|'
92
- format_env:=$(call censor,SECRET) | \
93
- $(call censor,PASSWORD) | \
94
- $(call censor,TOKEN) | \
95
- $(call censor,PRIVATE) | \
96
- $(call censor,KEY) | \
97
- ${censor_urls} | \
98
- sed 's/\(^[a-zA-Z0-9_]\+\)=/${blue}\1${reset}=/' | \
99
- sed 's/^/ /' | \
100
- sed 's/=\(.*\)$$/=${yellow}\1${reset}/'
91
+ censor=s/\(^${key_chars}*$(1)${key_chars}*\)=\(.*\)$$/\1=${censored}/
92
+ censor_urls:=s|\([a-zA-Z0-9_+]\+://.*:\).*\(@.*\)$$|\1${censored}\2|
93
+ format_env:=sed '$(call censor,SECRET); \
94
+ $(call censor,TOKEN); \
95
+ $(call censor,PRIVATE); \
96
+ $(call censor,KEY); \
97
+ $(censor_urls); \
98
+ /^\s*$$/d; \
99
+ /^\s*\#/d; \
100
+ s/\(^[a-zA-Z0-9_]\+\)=/${blue}\1${reset}=/; \
101
+ s/^/ /; \
102
+ s/=\(.*\)$$/=${yellow}\1${reset}/' | \
103
+ sort
101
104
 
102
105
  fail=( \
103
106
  $(call printraw,' ${cross}') ; \
@@ -38,9 +38,15 @@ ifneq (,$(wildcard ${env_file}))
38
38
  rake=. ${env_file} && ${rake_cmd}
39
39
  endif
40
40
 
41
- docker_config:=$(shell ${rake} orchestration:config)
42
- docker_organization=$(word 1,$(docker_config))
43
- docker_repository=$(word 2,$(docker_config))
41
+ ifeq (,$(findstring serve,$(MAKECMDGOALS)))
42
+ ifeq (,$(findstring console,$(MAKECMDGOALS)))
43
+ ifeq (,$(findstring test,$(MAKECMDGOALS)))
44
+ docker_config:=$(shell RAILS_ENV=development bundle exec rake orchestration:config)
45
+ docker_organization=$(word 1,$(docker_config))
46
+ docker_repository=$(word 2,$(docker_config))
47
+ endif
48
+ endif
49
+ endif
44
50
 
45
51
  ifeq (,$(project_name))
46
52
  project_base = ${docker_repository}_${env}
@@ -74,7 +80,9 @@ else
74
80
  compose_project_name = ${project_base}
75
81
  endif
76
82
 
77
- compose_base=env HOST_UID=$(shell id -u) \
83
+ compose_base=env -i \
84
+ PATH=$(PATH) \
85
+ HOST_UID=$(shell id -u) \
78
86
  DOCKER_ORGANIZATION="${docker_organization}" \
79
87
  DOCKER_REPOSITORY="${docker_repository}" \
80
88
  COMPOSE_PROJECT_NAME="${compose_project_name}" \
@@ -106,8 +114,8 @@ all: build
106
114
  ifndef network
107
115
  start: network := ${compose_project_name}_default
108
116
  endif
109
- start: _clean-logs
110
- @$(call print,'${yellow}Starting containers${reset} ...')
117
+ start: _create-log-directory _clean-logs
118
+ @$(call print,'${yellow}Starting ${cyan}${env}${yellow} containers${reset} ...')
111
119
  ifeq (${env},$(filter ${env},test development))
112
120
  @${compose} up --detach --force-recreate --renew-anon-volumes --remove-orphans ${services} ${log} || ${fail}
113
121
  @[ -n '${sidecar}' ] && \
@@ -138,7 +146,7 @@ start-<%= service %>:
138
146
  .PHONY: stop
139
147
  stop: network := ${compose_project_name}_default
140
148
  stop:
141
- @$(call print,'${yellow}Stopping containers${reset} ...')
149
+ @$(call print,'${yellow}Stopping ${cyan}${env}${yellow} containers${reset} ...')
142
150
  @if docker ps --format "{{.ID}}" | grep -q $(shell hostname) ; \
143
151
  then \
144
152
  ( docker network disconnect ${network} $(shell hostname) ${log} || : ) \
@@ -171,7 +179,7 @@ serve:
171
179
  then ( \
172
180
  $(call println,'${yellow}Environment${reset}: ${green}${env_file}${reset}') && \
173
181
  cat '${env_file}' | ${format_env} && \
174
- $(call println,'') \
182
+ $(call printrawln,'') \
175
183
  ) ; \
176
184
  fi
177
185
  ${rails}
@@ -184,7 +192,7 @@ console:
184
192
  then ( \
185
193
  $(call println,'${yellow}Environment${reset}: ${green}${env_file}${reset}') && \
186
194
  cat '${env_file}' | ${format_env} && \
187
- $(call println,'') \
195
+ $(call printrawln,'') \
188
196
  ) ; \
189
197
  fi
190
198
  ${rails} console
@@ -210,7 +218,7 @@ endif
210
218
  dump:
211
219
  ifndef verbose
212
220
  @$(call println)
213
- @$(call println,'${yellow}Captured${reset} ${green}stdout${reset} ${yellow}and${reset} ${red}stderr${reset} ${yellow}log data${reset}:')
221
+ @$(call println,'${yellow}Captured${reset} ${green}stdout${reset} ${yellow}and${reset} ${red}stderr${reset} ${yellow}log data [${cyan}${env}${yellow}]${reset}:')
214
222
  @$(call println)
215
223
  @echo
216
224
  @test -f '${stdout}' && ( \
@@ -230,6 +238,7 @@ ifndef verbose
230
238
  $(call hr,${red}) ; \
231
239
  )
232
240
  endif
241
+ ifneq (,$(findstring deploy,$(MAKECMDGOALS)))
233
242
  @echo ; \
234
243
  $(call hr,${yellow}) ; \
235
244
  $(call println,'${gray}docker-compose logs${reset}') ; \
@@ -238,6 +247,8 @@ endif
238
247
  @${compose} logs
239
248
  @echo ; \
240
249
  $(call hr,${yellow})
250
+ endif
251
+ @$(NOOP)
241
252
 
242
253
  .PHONY: image
243
254
  image:
@@ -245,45 +256,40 @@ image:
245
256
 
246
257
  ### Deployment utility commands ###
247
258
 
248
- .PHONY: bundle
249
- bundle:
250
- ifndef path
251
- @$(warning Missing `path` parameter; using `./bundle.tar`. Set a custom path with `make bundle path=/tmp/bundle.tar`)
252
- endif
253
- @rm -rf ${orchestration_dir}/.deploy/
254
- @mkdir -p ${orchestration_dir}/.deploy/${docker_repository}/
255
- @sed -e "s/%%VERSION%%/${git_version}/g" \
256
- -e "s/%%REPOSITORY%%/${docker_repository}/g" \
257
- -e "s/%%ORGANIZATION%%/${docker_organization}/g" \
258
- ${orchestration_dir}/deploy.mk > \
259
- ${orchestration_dir}/.deploy/${docker_repository}/Makefile
260
- @bundle_path="${path}" ; tar -C '${orchestration_dir}/.deploy' -cf "$${bundle_path:-./bundle.tar}" ./${docker_repository}
261
-
262
259
  .PHONY: deploy
263
- deploy: path := $(shell mktemp -d)
260
+ ifdef env_file
261
+ deploy: env_file_option = --env-file ${env_file}
262
+ endif
264
263
  deploy: RAILS_ENV := ${env}
265
264
  deploy: RACK_ENV := ${env}
266
265
  deploy: DOCKER_TAG = ${git_version}
266
+ deploy: base_vars = DOCKER_ORGANIZATION=${docker_organization} DOCKER_REPOSITORY=${docker_repository} DOCKER_TAG=${git_version}
267
+ deploy: compose_deploy := ${base_vars} COMPOSE_PROJECT_NAME=${project_base} HOST_UID=$(shell id -u) docker-compose ${env_file_option} --project-name ${project_base} -f orchestration/docker-compose.${env}.yml
268
+ deploy: compose_config := ${compose_deploy} config
269
+ deploy: deploy_cmd := echo "$${config}" | ssh "${manager}" "/bin/bash -lc 'cat | docker stack deploy --prune --with-registry-auth -c - ${project_base}'"
270
+ deploy: out_of_sequence_error := rpc error: code = Unknown desc = update out of sequence
271
+ deploy: retry_message := ${yellow}Detected Docker RPC error: ${red}${out_of_sequence_error}${yellow}. Retrying in
267
272
  deploy:
268
273
  ifndef manager
269
274
  @$(call println_error,'Missing `manager` parameter: `make deploy manager=swarm-manager.example.com`') ; exit 1
270
275
  endif
271
- @$(call println,'${yellow}Deploying stack via${reset} ${green}${manager}${reset} ...') && \
276
+ @$(call println,'${yellow}Deploying ${green}${env} ${yellow}stack via ${green}${manager} ${yellow}as ${green}${project_base}${reset} ...') && \
272
277
  ( \
273
- $(call make,bundle path='${path}/bundle.tar') ${log} && \
274
- cd '${path}' ${log} && \
275
- tar xf 'bundle.tar' ${log} && \
276
- cd '${docker_repository}' ${log} && \
277
- ( [ -z '${env_file}' ] || cp '${env_file}' './.env' ${log} ) && \
278
- $(call println,'${yellow}Deployment environment${reset}:') && \
279
- ( test -f '.env' && cat '.env' | ${format_env} || : ) && \
280
- echo 'DOCKER_ORGANIZATION=${docker_organization}' >> './.env' && \
281
- echo 'DOCKER_REPOSITORY=${docker_repository}' >> './.env' && \
282
- echo 'DOCKER_TAG=${git_version}' >> ./.env && \
283
- $(call println,'') && \
284
- $(call println,'${yellow}Application image${reset}: ${cyan}${docker_image}${reset}') && \
285
- ${compose} config 2>${stderr} | ssh "${manager}" 'docker stack deploy --prune --with-registry-auth -c - "${project_base}"' ${log} && \
286
- ( [ -z "${path}" ] || rm -rf "${path}" ${log} ) \
278
+ $(call println,'${yellow}Deployment environment${reset}:') && \
279
+ ( test -f '${env_file}' && cat '${env_file}' | ${format_env} || : ) && \
280
+ $(call println,'') && \
281
+ $(call println,'${yellow}Application image${reset}: ${cyan}${docker_image}${reset}') && \
282
+ export config="$$(${compose_config} 2>${stderr})" ; \
283
+ config_exit_code=$$? ; \
284
+ if [[ "$${config_exit_code}" != "0" ]]; then exit ${config_exit_code}; fi ; \
285
+ output="$$(${deploy_cmd} | tee /dev/tty)" ; \
286
+ deploy_exit_code=$$? ; \
287
+ if [[ "$${deploy_exit_code}" == 0 ]] ; then exit 0 ; fi ; \
288
+ if ! echo "$${output}" | grep -q '${out_of_sequence_error}' ; then exit ${deploy_exit_code} ; fi ; \
289
+ retry_in="$$(( 10 + RANDOM % 50 ))" ; \
290
+ echo "${retry_message} ${green}$${retry_in} ${yellow}seconds.${reset}" ; \
291
+ sleep "$${retry_in}" ; \
292
+ ${deploy_cmd} \
287
293
  ) \
288
294
  || ${fail}
289
295
 
@@ -316,8 +322,9 @@ wait-listener:
316
322
  ### Docker build commands ###
317
323
 
318
324
  .PHONY: build
319
- build: context = ${orchestration_dir}/.build/context.tar
320
- build: check-local-changes
325
+ build: build_dir = ${orchestration_dir}/.build
326
+ build: context = ${build_dir}/context.tar
327
+ build: _create-log-directory check-local-changes
321
328
  @$(call print,'${yellow}Preparing build context from${reset} ${cyan}${git_branch}:${git_version}${reset} ... ')
322
329
  @mkdir -p ${orchestration_dir}/.build ${log} || ${fail}
323
330
  ifndef dev
@@ -330,6 +337,16 @@ else
330
337
  @tar -cvf '${context}' . ${log} || ${fail}
331
338
  endif
332
339
  @$(call printrawln,'${green}complete.${reset} ${tick}')
340
+ ifdef include
341
+ @$(call print,'${yellow}Including files from:${reset} ${cyan}${include}${reset} ...')
342
+ @(while read line; do \
343
+ export line; \
344
+ include_dir="${build_dir}/$$(dirname "$${line}")/" && \
345
+ mkdir -p "$${include_dir}" && cp "$${line}" "$${include_dir}" \
346
+ && (cd '${orchestration_dir}/.build/' && tar rf 'context.tar' "$${line}"); \
347
+ done < '${include}') ${log} || ${fail}
348
+ @$(call printrawln,' ${green}complete.${reset} ${tick}')
349
+ endif
333
350
  ifdef sidecar
334
351
  # Assume we are in a line-buffered environment (e.g. Jenkins)
335
352
  @$(call println,'${yellow}Building image${reset} ...')
@@ -348,7 +365,7 @@ endif
348
365
  @$(call println,'[${green}tag${reset}] ${cyan}${docker_organization}/${docker_repository}:${git_version}${reset}')
349
366
 
350
367
  .PHONY: push
351
- push:
368
+ push: _create-log-directory
352
369
  @$(call print,'${yellow}Pushing${reset} ${cyan}${docker_image}${reset} ...')
353
370
  @docker push ${docker_image} ${log_progress} || ${fail}
354
371
  @$(call printrawln,' ${green}complete${reset}. ${tick}')
@@ -370,3 +387,7 @@ endif
370
387
  _clean-logs:
371
388
  @rm -f '${stdout}' '${stderr}'
372
389
  @touch '${stdout}' '${stderr}'
390
+
391
+ .PHONY: _create-log-directory
392
+ _create-log-directory:
393
+ @mkdir -p log
@@ -20,15 +20,15 @@ module Orchestration
20
20
  @settings = settings
21
21
  end
22
22
 
23
- def write(desc, message, color_name = nil, newline = true)
23
+ def write(desc, message, color_name = nil, newline: true)
24
24
  output = newline ? "#{message}\n" : message.to_s
25
- STDOUT.print colorize(desc, output, color_name)
26
- STDOUT.flush
25
+ $stdout.print colorize(desc, output, color_name)
26
+ $stdout.flush
27
27
  end
28
28
 
29
29
  def read(message, default = nil)
30
- write(:input, prompt(message, default), nil, false)
31
- result = STDIN.gets.chomp.strip
30
+ write(:input, prompt(message, default), nil, newline: false)
31
+ result = $stdin.gets.chomp.strip
32
32
  return default if result.empty?
33
33
 
34
34
  result
@@ -57,7 +57,7 @@ module Orchestration
57
57
  COLOR_MAP.fetch(color_name)
58
58
  end
59
59
 
60
- Paint[desc.to_s.rjust(15), *color] + ' ' + message
60
+ "#{Paint[desc.to_s.rjust(15), *color]} #{message}"
61
61
  end
62
62
 
63
63
  def t(key)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Orchestration
4
- VERSION = '0.5.2'
4
+ VERSION = '0.5.7'
5
5
  end
@@ -28,7 +28,7 @@ namespace :orchestration do
28
28
 
29
29
  desc I18n.t('orchestration.rake.wait')
30
30
  task :wait do
31
- Orchestration::InstallGenerator.new.verify_makefile(false)
31
+ Orchestration::InstallGenerator.new.verify_makefile(skip: false)
32
32
  env = Orchestration::Environment.new
33
33
  services = Orchestration::Services
34
34
  env.docker_compose_config['services'].each do |name, _service|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.name = 'orchestration'
10
10
  spec.version = Orchestration::VERSION
11
11
  spec.authors = ['Bob Farrell']
12
- spec.email = ['robertanthonyfarrell@gmail.com']
12
+ spec.email = ['git@bob.frl']
13
13
 
14
14
  spec.summary = 'Docker orchestration toolkit'
15
15
  spec.description = 'Tools to help launch apps in Docker'
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
21
21
  end
22
22
  end
23
23
 
24
+ spec.required_ruby_version = '~> 2.6'
24
25
  spec.bindir = 'bin'
25
26
  spec.executables = []
26
27
  spec.require_paths = ['lib']
@@ -32,10 +33,9 @@ Gem::Specification.new do |spec|
32
33
  spec.add_runtime_dependency 'thor', '~> 1.0'
33
34
 
34
35
  spec.add_development_dependency 'activerecord', '~> 6.0'
35
- spec.add_development_dependency 'betterp', '~> 0.1.3'
36
36
  spec.add_development_dependency 'bundler', '~> 1.16'
37
37
  spec.add_development_dependency 'bunny', '~> 2.12'
38
- spec.add_development_dependency 'byebug', '~> 10.0'
38
+ spec.add_development_dependency 'devpack', '~> 0.3.0'
39
39
  spec.add_development_dependency 'mongoid', '~> 7.0'
40
40
  spec.add_development_dependency 'mysql2', '~> 0.5.2'
41
41
  spec.add_development_dependency 'pg', '~> 1.1'
@@ -43,8 +43,8 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency 'rake', '~> 10.0'
44
44
  spec.add_development_dependency 'rspec', '~> 3.0'
45
45
  spec.add_development_dependency 'rspec-its', '~> 1.2'
46
- spec.add_development_dependency 'rubocop', '~> 0.77.0'
46
+ spec.add_development_dependency 'rubocop', '~> 0.90.0'
47
47
  spec.add_development_dependency 'sqlite3', '~> 1.3'
48
- spec.add_development_dependency 'strong_versions', '~> 0.3.1'
48
+ spec.add_development_dependency 'strong_versions', '~> 0.4.5'
49
49
  spec.add_development_dependency 'webmock', '~> 3.4'
50
50
  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.5.2
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-23 00:00:00.000000000 Z
11
+ date: 2021-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: database_url
@@ -94,20 +94,6 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '6.0'
97
- - !ruby/object:Gem::Dependency
98
- name: betterp
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 0.1.3
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 0.1.3
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: bundler
113
99
  requirement: !ruby/object:Gem::Requirement
@@ -137,19 +123,19 @@ dependencies:
137
123
  - !ruby/object:Gem::Version
138
124
  version: '2.12'
139
125
  - !ruby/object:Gem::Dependency
140
- name: byebug
126
+ name: devpack
141
127
  requirement: !ruby/object:Gem::Requirement
142
128
  requirements:
143
129
  - - "~>"
144
130
  - !ruby/object:Gem::Version
145
- version: '10.0'
131
+ version: 0.3.0
146
132
  type: :development
147
133
  prerelease: false
148
134
  version_requirements: !ruby/object:Gem::Requirement
149
135
  requirements:
150
136
  - - "~>"
151
137
  - !ruby/object:Gem::Version
152
- version: '10.0'
138
+ version: 0.3.0
153
139
  - !ruby/object:Gem::Dependency
154
140
  name: mongoid
155
141
  requirement: !ruby/object:Gem::Requirement
@@ -254,14 +240,14 @@ dependencies:
254
240
  requirements:
255
241
  - - "~>"
256
242
  - !ruby/object:Gem::Version
257
- version: 0.77.0
243
+ version: 0.90.0
258
244
  type: :development
259
245
  prerelease: false
260
246
  version_requirements: !ruby/object:Gem::Requirement
261
247
  requirements:
262
248
  - - "~>"
263
249
  - !ruby/object:Gem::Version
264
- version: 0.77.0
250
+ version: 0.90.0
265
251
  - !ruby/object:Gem::Dependency
266
252
  name: sqlite3
267
253
  requirement: !ruby/object:Gem::Requirement
@@ -282,14 +268,14 @@ dependencies:
282
268
  requirements:
283
269
  - - "~>"
284
270
  - !ruby/object:Gem::Version
285
- version: 0.3.1
271
+ version: 0.4.5
286
272
  type: :development
287
273
  prerelease: false
288
274
  version_requirements: !ruby/object:Gem::Requirement
289
275
  requirements:
290
276
  - - "~>"
291
277
  - !ruby/object:Gem::Version
292
- version: 0.3.1
278
+ version: 0.4.5
293
279
  - !ruby/object:Gem::Dependency
294
280
  name: webmock
295
281
  requirement: !ruby/object:Gem::Requirement
@@ -306,7 +292,7 @@ dependencies:
306
292
  version: '3.4'
307
293
  description: Tools to help launch apps in Docker
308
294
  email:
309
- - robertanthonyfarrell@gmail.com
295
+ - git@bob.frl
310
296
  executables: []
311
297
  extensions: []
312
298
  extra_rdoc_files: []
@@ -322,10 +308,7 @@ files:
322
308
  - README.md
323
309
  - Rakefile
324
310
  - bin/console
325
- - bin/rspec
326
- - bin/rubocop
327
311
  - bin/setup
328
- - bin/strong_versions
329
312
  - config/locales/en.yml
330
313
  - doc/images/example.png
331
314
  - lib/Rakefile
@@ -373,7 +356,6 @@ files:
373
356
  - lib/orchestration/templates/Dockerfile.erb
374
357
  - lib/orchestration/templates/application.mk.erb
375
358
  - lib/orchestration/templates/database.yml.erb
376
- - lib/orchestration/templates/deploy.mk.erb
377
359
  - lib/orchestration/templates/entrypoint.sh.erb
378
360
  - lib/orchestration/templates/env.erb
379
361
  - lib/orchestration/templates/makefile_macros.mk.erb
@@ -395,9 +377,9 @@ require_paths:
395
377
  - lib
396
378
  required_ruby_version: !ruby/object:Gem::Requirement
397
379
  requirements:
398
- - - ">="
380
+ - - "~>"
399
381
  - !ruby/object:Gem::Version
400
- version: '0'
382
+ version: '2.6'
401
383
  required_rubygems_version: !ruby/object:Gem::Requirement
402
384
  requirements:
403
385
  - - ">="
data/bin/rspec DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rspec' 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("rspec-core", "rspec")
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rubocop' 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("rubocop", "rubocop")
@@ -1,29 +0,0 @@
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,69 +0,0 @@
1
- -include .env
2
- export
3
-
4
- ifneq (,$(RAILS_ENV))
5
- env:=$(RAILS_ENV)
6
- else ifneq (,$(RACK_ENV))
7
- env:=$(RACK_ENV)
8
- endif
9
-
10
- verify-environment:
11
- ifndef CONTAINER_PORT
12
- @$(error `CONTAINER_PORT` must be defined in environment)
13
- endif
14
-
15
- ifndef env
16
- @$(error Either `env`, `RACK_ENV` or `RAILS_ENV` must be defined in environment)
17
- endif
18
-
19
- project_name:=%%REPOSITORY%%_${env}
20
- compose_base:=env HOST_UID=$(shell id -u) \
21
- DOCKER_ORGANIZATION=%%ORGANIZATION%% \
22
- DOCKER_REPOSITORY=%%REPOSITORY%%:%%VERSION%% \
23
- docker-compose \
24
- -p ${project_name} \
25
- -f docker-compose.production.yml
26
-
27
- .PHONY: deploy
28
- deploy:
29
- ifndef manager
30
- @$(error Missing `manager` parameter: `make deploy manager=swarm-manager.example.com`)
31
- else
32
- @tar -cf - . | ssh ${manager} 'cd $$(mktemp -d) ; chmod 0700 . ; cat - | tar -x ; make deploy-stack ; rm -r $$(pwd)'
33
- endif
34
-
35
- .PHONY: deploy-stack
36
- deploy-stack:
37
- ${compose} config | docker stack deploy --prune --with-registry-auth -c - ${project_name}
38
-
39
- .PHONY: console
40
- service := app
41
- command := /bin/bash
42
- console:
43
- @echo "Creating temporary container..."
44
- @${compose} run --rm ${service} ${command}
45
-
46
- .PHONY: config
47
- config:
48
- @${compose} config
49
-
50
- .PHONY: pull
51
- pull:
52
- @${compose} pull
53
-
54
- .PHONY: logs
55
- logs: service := app
56
- logs:
57
- ifndef manager
58
- @$(error Missing `manager` parameter: `make logs manager=swarm-manager.example.com`)
59
- else
60
- ssh ${manager} "docker service logs -f ${project_name}_${service}"
61
- endif
62
-
63
- .PHONY: migrate
64
- migrate:
65
- @${compose} run --rm app bundle exec rake db:migrate
66
-
67
- .PHONY: compose
68
- compose:
69
- @echo ${compose}