OSDN Git Service

f36a4013c5cd90376883dfba310cb009c5f031d0
[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, 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'] == '2'
91 end
92
93 config_format_version = node['concourse-ci']['docker-compose']['config_format_version']
94
95 # if config_format_version == '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 == '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 web_envs['CONCOURSE_EXTERNAL_URL'] = "http://#{node['ipaddress']}:8080" if external_url.nil?
206
207 data_source = web_envs_org['CONCOURSE_POSTGRES_DATA_SOURCE']
208 # for backward compatibility.
209 data_source = data_source.gsub(/<POSTGRES_PASSWORD>/, '${POSTGRES_PASSWORD}')
210 web_envs['CONCOURSE_POSTGRES_DATA_SOURCE'] = data_source
211
212 template "#{bin_dir}/concourse_up" do
213   source 'opt/docker-compose/app/concourse/bin/concourse_up'
214   owner 'root'
215   group 'root'
216   mode '0755'
217   action :create
218 end
219
220 if node['concourse-ci']['with_ssl_cert_cookbook']
221   ::Chef::Recipe.send(:include, SSLCert::Helper)
222   cn = node['concourse-ci']['ssl_cert']['common_name']
223   append_server_ssl_cn(cn)
224   include_recipe 'ssl_cert::server_key_pairs'
225
226   # Concourse web process owner is root.
227   web_vols.push("#{server_cert_path(cn)}:/root/server.crt:ro")
228   web_vols.push("#{server_key_path(cn)}:/root/server.key:ro")
229   web_envs['CONCOURSE_TLS_CERT'] = '/root/server.crt'
230   web_envs['CONCOURSE_TLS_KEY'] = '/root/server.key'
231 end
232
233 # Worker
234 worker_vols = config_srvs['concourse-worker']['volumes'].to_a
235 worker_vols.push("#{node['concourse-ci']['docker-compose']['worker_keys_dir']}:/concourse-keys")
236
237 # Common
238 if node['concourse-ci']['docker-compose']['import_ca']
239   ::Chef::Recipe.send(:include, SSLCert::Helper)
240
241   node['concourse-ci']['ssl_cert']['ca_names'].each {|ca_name|
242     append_ca_name(ca_name)
243     ca_cert_vol = "#{ca_cert_path(ca_name)}:/usr/share/ca-certificates/#{ca_name}.crt:ro"
244     web_vols.push(ca_cert_vol)
245     worker_vols.push(ca_cert_vol)
246   }
247   include_recipe 'ssl_cert::ca_certs'
248
249   import_ca_script = '/usr/local/bin/concourse_import_ca'
250   template "#{bin_dir}/concourse_import_ca" do
251     source 'opt/docker-compose/app/concourse/bin/concourse_import_ca'
252     owner 'root'
253     group 'root'
254     mode '0755'
255     action :create
256   end
257   import_ca_script_vol = "#{bin_dir}/concourse_import_ca:#{import_ca_script}:ro"
258   web_vols.push(import_ca_script_vol)
259   worker_vols.push(import_ca_script_vol)
260
261   image_entrypoint = node['concourse-ci']['docker-image']['entrypoint']
262   override_config_srvs['concourse-web']['entrypoint'] \
263     = "/bin/sh -c \"#{import_ca_script} && #{image_entrypoint} web\""
264   override_config_srvs['concourse-worker']['entrypoint'] \
265     = "/bin/sh -c \"#{import_ca_script} && #{image_entrypoint} worker\""
266   if config_format_version == '2'
267     node.rm('concourse-ci', 'docker-compose', 'config', 'services', 'concourse-web', 'command')
268     node.rm('concourse-ci', 'docker-compose', 'config', 'services', 'concourse-worker', 'command')
269   else
270     node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-web', 'command')
271     node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-worker', 'command')
272   end
273 end
274
275 # merge environment hash
276 force_override_config_srvs['concourse-web']['environment'] = web_envs unless web_envs.empty?
277 # reset vlumes array.
278 override_config_srvs['concourse-web']['volumes'] = web_vols unless web_vols.empty?
279 override_config_srvs['concourse-worker']['volumes'] = worker_vols unless worker_vols.empty?
280
281 template env_file do
282   source 'opt/docker-compose/app/concourse/.env'
283   owner 'root'
284   group 'root'
285   mode '0600'
286   sensitive true
287   # prevent Chef from logging password attribute value.
288   variables(
289     # secrets
290     db_passwd: db_passwd,
291     encryption_key: encryption_key,
292     basic_auth_passwd: basic_auth_passwd,
293     oauth_client_id: oauth_client_id,
294     oauth_client_secret: oauth_client_secret
295   )
296 end
297
298 template config_file do
299   source  'opt/docker-compose/app/concourse/docker-compose.yml'
300   owner 'root'
301   group 'root'
302   mode '0600'
303 end
304
305 log <<-"EOM"
306 Note: You must execute the following command manually.
307   See #{doc_url}
308   * Start:
309     $ cd #{app_dir}
310     $ sudo docker-compose up
311   * Stop
312     $ sudo docker-compose down
313 EOM