OSDN Git Service

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