OSDN Git Service

adds the `concourse_start` script.
[metasearch/grid-chef-repo.git] / cookbooks / concourse-ci / recipes / docker-compose.rb
1 #
2 # Cookbook Name:: concourse-ci
3 # Recipe:: docker-compose
4 #
5 # Copyright 2017-2018, whitestar
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 #     http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19
20 require 'securerandom'
21
22 doc_url = 'https://concourse.ci/docker-repository.html'
23
24 include_recipe 'platform_utils::kernel_user_namespace'
25 include_recipe 'docker-grid::compose'
26
27 app_dir = node['concourse-ci']['docker-compose']['app_dir']
28 bin_dir = "#{app_dir}/bin"
29 pgdata_dir = node['concourse-ci']['docker-compose']['pgdata_dir']
30 web_keys_dir = node['concourse-ci']['docker-compose']['web_keys_dir']
31 worker_keys_dir = node['concourse-ci']['docker-compose']['worker_keys_dir']
32
33 [
34   app_dir,
35   bin_dir,
36   web_keys_dir,
37   worker_keys_dir,
38 ].each {|dir|
39   resources(directory: dir) rescue directory dir do
40     owner 'root'
41     group 'root'
42     mode '0755'
43     recursive true
44   end
45 }
46
47 # DB persistent
48 resources(directory: pgdata_dir) rescue directory pgdata_dir do
49   #owner 999
50   group 'root'
51   mode '0700'
52   recursive true
53 end if !pgdata_dir.nil? && !pgdata_dir.empty?
54
55 bash 'ssh-keygen_keys' do
56   code <<-"EOH"
57     rm -f #{web_keys_dir}/*
58     ssh-keygen -t rsa -f #{web_keys_dir}/tsa_host_key -N ''
59     ssh-keygen -t rsa -f #{web_keys_dir}/session_signing_key -N ''
60     rm -f #{worker_keys_dir}/*
61     ssh-keygen -t rsa -f #{worker_keys_dir}/worker_key -N ''
62     cp #{worker_keys_dir}/worker_key.pub #{web_keys_dir}/authorized_worker_keys
63     cp #{web_keys_dir}/tsa_host_key.pub #{worker_keys_dir}
64   EOH
65   action :run
66   unless node['concourse-ci']['docker-compose']['ssh_keys_reset']
67     not_if { File.exist?("#{web_keys_dir}/tsa_host_key") }
68     not_if { File.exist?("#{web_keys_dir}/session_signing_key") }
69     not_if { File.exist?("#{worker_keys_dir}/worker_key") }
70   end
71 end
72
73 env_file = "#{app_dir}/.env"
74 config_file = "#{app_dir}/docker-compose.yml"
75
76 env_local = nil
77 if File.exist?(env_file)
78   env_local = {}
79   File.open(env_file) do |file|
80     file.each_line do |line|
81       env_local[$1] = $2 if line =~ /^([^=]*)=(.*)$/
82     end
83   end
84 end
85
86 config_srvs_local = nil
87 if File.exist?(config_file)
88   require 'yaml'
89   config_srvs_local = YAML.load_file(config_file)
90   config_srvs_local = config_srvs_local['services'] if config_srvs_local.key?('version') && config_srvs_local['version'].to_i >= 2
91 end
92
93 config_format_version = node['concourse-ci']['docker-compose']['config_format_version']
94
95 # if config_format_version.to_i == 1
96 config_srvs = node['concourse-ci']['docker-compose']['config']
97 override_config_srvs = node.override['concourse-ci']['docker-compose']['config']
98 force_override_config_srvs = node.force_override['concourse-ci']['docker-compose']['config']
99 if config_format_version.to_i == 2
100   config_srvs = config_srvs['services']
101   override_config_srvs = override_config_srvs['services']
102   force_override_config_srvs = force_override_config_srvs['services']
103 end
104
105 # Database
106 db_envs_org = config_srvs['concourse-db']['environment']
107 db_envs = {}
108 db_vols = config_srvs['concourse-db']['volumes'].to_a
109
110 db_password_reset = node['concourse-ci']['docker-compose']['db_password_reset']
111 db_passwd = nil
112 db_password_vault_item = node['concourse-ci']['docker-compose']['db_password_vault_item']
113 if !db_password_vault_item.empty?
114   # 1. from Chef Vault (recommended).
115   db_passwd = get_vault_item_value(db_password_vault_item)
116 else
117   # 2. from Chef attribute (NOT recommended).
118   db_passwd = db_envs_org['POSTGRES_PASSWORD']
119   if db_passwd.nil? || db_passwd.empty?
120     db_passwd = \
121       if !config_srvs_local.nil? \
122         && config_srvs_local['concourse-db']['environment']['POSTGRES_PASSWORD'] != '${POSTGRES_PASSWORD}' \
123         && !db_password_reset
124         # 3. preserve it from the local docker-compose.yml file for backward compatibility.
125         config_srvs_local['concourse-db']['environment']['POSTGRES_PASSWORD']
126       elsif !env_local.nil? && !env_local['POSTGRES_PASSWORD'].nil? && !db_password_reset
127         # 4. preserve it from the local .env file.
128         env_local['POSTGRES_PASSWORD']
129       else
130         # 5. auto generate.
131         SecureRandom.hex  # or urlsafe_base64
132       end
133   end
134 end
135 # prevent Chef from logging password attribute value. (=> template variables)
136 db_envs['POSTGRES_PASSWORD'] = '${POSTGRES_PASSWORD}'
137
138 db_vols.push("#{pgdata_dir}:#{db_envs_org['PGDATA']}") if !pgdata_dir.nil? && !pgdata_dir.empty?
139
140 # merge environment hash
141 force_override_config_srvs['concourse-db']['environment'] = db_envs unless db_envs.empty?
142 # reset vlumes array.
143 override_config_srvs['concourse-db']['volumes'] = db_vols unless db_vols.empty?
144
145 # Web
146 web_envs_org = config_srvs['concourse-web']['environment']
147 web_envs = {}
148 web_vols = config_srvs['concourse-web']['volumes'].to_a
149
150 web_ports = config_srvs['concourse-web']['ports']
151 override_config_srvs['concourse-web']['ports'] = ['8080:8080'] if web_ports.empty?
152
153 web_vols.push("#{node['concourse-ci']['docker-compose']['web_keys_dir']}:/concourse-keys")
154
155 encryption_key = nil
156 encryption_key_vault_item = node['concourse-ci']['docker-compose']['web_encryption_key_vault_item']
157 unless encryption_key_vault_item.empty?
158   encryption_key = get_vault_item_value(encryption_key_vault_item)
159   web_envs['CONCOURSE_ENCRYPTION_KEY'] = '${CONCOURSE_ENCRYPTION_KEY}'
160 end
161
162 web_password_reset = node['concourse-ci']['docker-compose']['web_password_reset']
163 basic_auth_passwd = nil
164 web_password_vault_item = node['concourse-ci']['docker-compose']['web_password_vault_item']
165 if !web_password_vault_item.empty?
166   # 1. from Chef Vault (recommended).
167   basic_auth_passwd = get_vault_item_value(web_password_vault_item)
168 else
169   # 2. from Chef attribute (NOT recommended).
170   basic_auth_passwd = web_envs_org['CONCOURSE_BASIC_AUTH_PASSWORD']
171   if basic_auth_passwd.nil? || basic_auth_passwd.empty?
172     basic_auth_passwd = \
173       if !config_srvs_local.nil? \
174         && config_srvs_local['concourse-web']['environment']['CONCOURSE_BASIC_AUTH_PASSWORD'] != '${CONCOURSE_BASIC_AUTH_PASSWORD}' \
175         && !web_password_reset
176         # 3. preserve it from the local docker-compose.yml file for backward compatibility.
177         config_srvs_local['concourse-web']['environment']['CONCOURSE_BASIC_AUTH_PASSWORD']
178       elsif !env_local.nil? && !env_local['CONCOURSE_BASIC_AUTH_PASSWORD'].nil? && !web_password_reset
179         # 4. preserve it from the local .env file.
180         env_local['CONCOURSE_BASIC_AUTH_PASSWORD']
181       else
182         # 5. auto generate.
183         SecureRandom.hex  # or urlsafe_base64
184       end
185   end
186 end
187 # prevent Chef from logging password attribute value. (=> template variables)
188 web_envs['CONCOURSE_BASIC_AUTH_PASSWORD'] = '${CONCOURSE_BASIC_AUTH_PASSWORD}'
189
190 oauth_client_id = nil
191 oauth_client_id_vault_item = node['concourse-ci']['docker-compose']['web_oauth_client_id_vault_item']
192 unless oauth_client_id_vault_item.empty?
193   oauth_client_id = get_vault_item_value(oauth_client_id_vault_item)
194   web_envs['CONCOURSE_GENERIC_OAUTH_CLIENT_ID'] = '${CONCOURSE_GENERIC_OAUTH_CLIENT_ID}'
195 end
196
197 oauth_client_secret = nil
198 oauth_client_secret_vault_item = node['concourse-ci']['docker-compose']['web_oauth_client_secret_vault_item']
199 unless oauth_client_secret_vault_item.empty?
200   oauth_client_secret = get_vault_item_value(oauth_client_secret_vault_item)
201   web_envs['CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET'] = '${CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET}'
202 end
203
204 external_url = web_envs_org['CONCOURSE_EXTERNAL_URL']
205 external_url = "http://#{node['ipaddress']}:8080" if external_url.nil?
206 web_envs['CONCOURSE_EXTERNAL_URL'] = external_url
207
208 data_source = web_envs_org['CONCOURSE_POSTGRES_DATA_SOURCE']
209 # for backward compatibility.
210 data_source = data_source.gsub(/<POSTGRES_PASSWORD>/, '${POSTGRES_PASSWORD}')
211 web_envs['CONCOURSE_POSTGRES_DATA_SOURCE'] = data_source
212
213 template "#{bin_dir}/concourse_up" do
214   source 'opt/docker-compose/app/concourse/bin/concourse_up'
215   owner 'root'
216   group 'root'
217   mode '0755'
218   action :create
219 end
220
221 if node['concourse-ci']['with_ssl_cert_cookbook']
222   ::Chef::Recipe.send(:include, SSLCert::Helper)
223   cn = node['concourse-ci']['ssl_cert']['common_name']
224   append_server_ssl_cn(cn)
225   include_recipe 'ssl_cert::server_key_pairs'
226
227   # Concourse web process owner is root.
228   web_vols.push("#{server_cert_path(cn)}:/root/server.crt:ro")
229   web_vols.push("#{server_key_path(cn)}:/root/server.key:ro")
230   web_envs['CONCOURSE_TLS_CERT'] = '/root/server.crt'
231   web_envs['CONCOURSE_TLS_KEY'] = '/root/server.key'
232 end
233
234 # Worker
235 worker_vols = config_srvs['concourse-worker']['volumes'].to_a
236 worker_vols.push("#{node['concourse-ci']['docker-compose']['worker_keys_dir']}:/concourse-keys")
237
238 # Common
239 if node['concourse-ci']['docker-compose']['import_ca']
240   ::Chef::Recipe.send(:include, SSLCert::Helper)
241
242   node['concourse-ci']['ssl_cert']['ca_names'].each {|ca_name|
243     append_ca_name(ca_name)
244     ca_cert_vol = "#{ca_cert_path(ca_name)}:/usr/share/ca-certificates/#{ca_name}.crt:ro"
245     web_vols.push(ca_cert_vol)
246     worker_vols.push(ca_cert_vol)
247   }
248   include_recipe 'ssl_cert::ca_certs'
249
250   import_ca_script = '/usr/local/bin/concourse_import_ca'
251   template "#{bin_dir}/concourse_import_ca" do
252     source 'opt/docker-compose/app/concourse/bin/concourse_import_ca'
253     owner 'root'
254     group 'root'
255     mode '0755'
256     action :create
257   end
258   import_ca_script_vol = "#{bin_dir}/concourse_import_ca:#{import_ca_script}:ro"
259   web_vols.push(import_ca_script_vol)
260   worker_vols.push(import_ca_script_vol)
261
262   image_entrypoint = node['concourse-ci']['docker-image']['entrypoint']
263   override_config_srvs['concourse-web']['entrypoint'] \
264     = "/bin/sh -c \"#{import_ca_script} && #{image_entrypoint} web\""
265   override_config_srvs['concourse-worker']['entrypoint'] \
266     = "/bin/sh -c \"#{import_ca_script} && #{image_entrypoint} worker\""
267   if config_format_version.to_i == 2
268     node.rm('concourse-ci', 'docker-compose', 'config', 'services', 'concourse-web', 'command')
269     node.rm('concourse-ci', 'docker-compose', 'config', 'services', 'concourse-worker', 'command')
270   else
271     node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-web', 'command')
272     node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-worker', 'command')
273   end
274 end
275
276 # merge environment hash
277 force_override_config_srvs['concourse-web']['environment'] = web_envs unless web_envs.empty?
278 # reset vlumes array.
279 override_config_srvs['concourse-web']['volumes'] = web_vols unless web_vols.empty?
280 override_config_srvs['concourse-worker']['volumes'] = worker_vols unless worker_vols.empty?
281
282 template env_file do
283   source 'opt/docker-compose/app/concourse/.env'
284   owner 'root'
285   group 'root'
286   mode '0600'
287   sensitive true
288   # prevent Chef from logging password attribute value.
289   variables(
290     # secrets
291     db_passwd: db_passwd,
292     encryption_key: encryption_key,
293     basic_auth_passwd: basic_auth_passwd,
294     oauth_client_id: oauth_client_id,
295     oauth_client_secret: oauth_client_secret
296   )
297 end
298
299 template config_file do
300   source  'opt/docker-compose/app/concourse/docker-compose.yml'
301   owner 'root'
302   group 'root'
303   mode '0600'
304 end
305
306 template "#{bin_dir}/fly_prune_workers_main" do
307   source 'opt/docker-compose/app/concourse/bin/fly_prune_workers_main'
308   owner 'root'
309   group 'root'
310   mode '0755'
311   action :create
312   variables(
313     basic_auth_username: web_envs_org['CONCOURSE_BASIC_AUTH_USERNAME'],
314     external_url: external_url
315   )
316 end
317
318 template "#{bin_dir}/concourse_start" do
319   source 'opt/docker-compose/app/concourse/bin/concourse_start'
320   owner 'root'
321   group 'root'
322   mode '0755'
323   action :create
324 end
325
326 log <<-"EOM"
327 Note: You must execute the following command manually.
328   See #{doc_url}
329   * Start:
330     $ cd #{app_dir}
331     $ sudo docker-compose up
332   * Stop
333     $ sudo docker-compose down
334 EOM