2 # Cookbook Name:: concourse-ci
3 # Recipe:: docker-compose
5 # Copyright 2017-2018, whitestar
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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.
20 require 'securerandom'
22 doc_url = 'https://concourse.ci/docker-repository.html'
24 include_recipe 'platform_utils::kernel_user_namespace'
25 include_recipe 'docker-grid::compose'
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']
39 resources(directory: dir) rescue directory dir do
48 resources(directory: pgdata_dir) rescue directory pgdata_dir do
53 end if !pgdata_dir.nil? && !pgdata_dir.empty?
55 bash 'ssh-keygen_keys' do
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}
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") }
73 env_file = "#{app_dir}/.env"
74 config_file = "#{app_dir}/docker-compose.yml"
77 if File.exist?(env_file)
79 File.open(env_file) do |file|
80 file.each_line do |line|
81 env_local[$1] = $2 if line =~ /^([^=]*)=(.*)$/
86 config_srvs_local = nil
87 if File.exist?(config_file)
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
93 config_format_version = node['concourse-ci']['docker-compose']['config_format_version']
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']
106 db_envs_org = config_srvs['concourse-db']['environment']
108 db_vols = config_srvs['concourse-db']['volumes'].to_a
110 db_password_reset = node['concourse-ci']['docker-compose']['db_password_reset']
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)
117 # 2. from Chef attribute (NOT recommended).
118 db_passwd = db_envs_org['POSTGRES_PASSWORD']
119 if db_passwd.nil? || db_passwd.empty?
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']
131 SecureRandom.hex # or urlsafe_base64
135 # prevent Chef from logging password attribute value. (=> template variables)
136 db_envs['POSTGRES_PASSWORD'] = '${POSTGRES_PASSWORD}'
138 db_vols.push("#{pgdata_dir}:#{db_envs_org['PGDATA']}") if !pgdata_dir.nil? && !pgdata_dir.empty?
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?
146 web_envs_org = config_srvs['concourse-web']['environment']
148 web_vols = config_srvs['concourse-web']['volumes'].to_a
150 web_ports = config_srvs['concourse-web']['ports']
151 override_config_srvs['concourse-web']['ports'] = ['8080:8080'] if web_ports.empty?
153 web_vols.push("#{node['concourse-ci']['docker-compose']['web_keys_dir']}:/concourse-keys")
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}'
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)
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']
183 SecureRandom.hex # or urlsafe_base64
187 # prevent Chef from logging password attribute value. (=> template variables)
188 web_envs['CONCOURSE_BASIC_AUTH_PASSWORD'] = '${CONCOURSE_BASIC_AUTH_PASSWORD}'
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}'
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}'
204 external_url = web_envs_org['CONCOURSE_EXTERNAL_URL']
205 web_envs['CONCOURSE_EXTERNAL_URL'] = "http://#{node['ipaddress']}:8080" if external_url.nil?
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
212 template "#{bin_dir}/concourse_up" do
213 source 'opt/docker-compose/app/concourse/bin/concourse_up'
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'
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'
234 worker_vols = config_srvs['concourse-worker']['volumes'].to_a
235 worker_vols.push("#{node['concourse-ci']['docker-compose']['worker_keys_dir']}:/concourse-keys")
238 if node['concourse-ci']['docker-compose']['import_ca']
239 ::Chef::Recipe.send(:include, SSLCert::Helper)
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)
247 include_recipe 'ssl_cert::ca_certs'
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'
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)
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.to_i == 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')
270 node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-web', 'command')
271 node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-worker', 'command')
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?
282 source 'opt/docker-compose/app/concourse/.env'
287 # prevent Chef from logging password attribute value.
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
298 template config_file do
299 source 'opt/docker-compose/app/concourse/docker-compose.yml'
306 Note: You must execute the following command manually.
310 $ sudo docker-compose up
312 $ sudo docker-compose down