2 # Cookbook Name:: concourse-ci
3 # Recipe:: docker-compose
5 # Copyright 2017, 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 ::Chef::Recipe.send(:include, SSLCert::Helper)
22 require 'securerandom'
24 doc_url = 'https://concourse.ci/docker-repository.html'
26 include_recipe 'platform_utils::kernel_user_namespace'
27 include_recipe 'docker-grid::compose'
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']
41 resources(directory: dir) rescue directory dir do
50 resources(directory: pgdata_dir) rescue directory pgdata_dir do
55 end if !pgdata_dir.nil? && !pgdata_dir.empty?
57 bash 'ssh-keygen_keys' do
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}
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") }
75 env_file = "#{app_dir}/.env"
76 config_file = "#{app_dir}/docker-compose.yml"
79 if File.exist?(env_file)
81 File.open(env_file) do |file|
82 file.each_line do |line|
83 env_local[$1] = $2 if line =~ /^(.*)=(.*)$/
88 config_srvs_local = nil
89 if File.exist?(config_file)
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'
95 config_format_version = node['concourse-ci']['docker-compose']['config_format_version']
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']
108 db_envs_org = config_srvs['concourse-db']['environment']
110 db_vols = config_srvs['concourse-db']['volumes'].to_a
112 db_password_reset = node['concourse-ci']['docker-compose']['db_password_reset']
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)
119 # 2. from Chef attribute (NOT recommended).
120 db_passwd = db_envs_org['POSTGRES_PASSWORD']
121 if db_passwd.nil? || db_passwd.empty?
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']
133 SecureRandom.hex # or urlsafe_base64
137 # prevent Chef from logging password attribute value. (=> template variables)
138 db_envs['POSTGRES_PASSWORD'] = '${POSTGRES_PASSWORD}'
140 db_vols.push("#{pgdata_dir}:#{db_envs_org['PGDATA']}") if !pgdata_dir.nil? && !pgdata_dir.empty?
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?
148 web_envs_org = config_srvs['concourse-web']['environment']
150 web_vols = config_srvs['concourse-web']['volumes'].to_a
152 web_ports = config_srvs['concourse-web']['ports']
153 override_config_srvs['concourse-web']['ports'] = ['8080:8080'] if web_ports.empty?
155 web_vols.push("#{node['concourse-ci']['docker-compose']['web_keys_dir']}:/concourse-keys")
157 web_password_reset = node['concourse-ci']['docker-compose']['web_password_reset']
158 basic_auth_passwd = nil
159 web_password_vault_item = node['concourse-ci']['docker-compose']['web_password_vault_item']
160 if !web_password_vault_item.empty?
161 # 1. from Chef Vault (recommended).
162 basic_auth_passwd = get_vault_item_value(web_password_vault_item)
164 # 2. from Chef attribute (NOT recommended).
165 basic_auth_passwd = web_envs_org['CONCOURSE_BASIC_AUTH_PASSWORD']
166 if basic_auth_passwd.nil? || basic_auth_passwd.empty?
167 basic_auth_passwd = \
168 if !config_srvs_local.nil? \
169 && config_srvs_local['concourse-web']['environment']['CONCOURSE_BASIC_AUTH_PASSWORD'] != '${CONCOURSE_BASIC_AUTH_PASSWORD}' \
170 && !web_password_reset
171 # 3. preserve it from the local docker-compose.yml file for backward compatibility.
172 config_srvs_local['concourse-web']['environment']['CONCOURSE_BASIC_AUTH_PASSWORD']
173 elsif !env_local.nil? && !env_local['CONCOURSE_BASIC_AUTH_PASSWORD'].nil? && !web_password_reset
174 # 4. preserve it from the local .env file.
175 env_local['CONCOURSE_BASIC_AUTH_PASSWORD']
178 SecureRandom.hex # or urlsafe_base64
182 # prevent Chef from logging password attribute value. (=> template variables)
183 web_envs['CONCOURSE_BASIC_AUTH_PASSWORD'] = '${CONCOURSE_BASIC_AUTH_PASSWORD}'
185 oauth_client_id = nil
186 oauth_client_id_vault_item = node['concourse-ci']['docker-compose']['web_oauth_client_id_vault_item']
187 unless oauth_client_id_vault_item.empty?
188 oauth_client_id = get_vault_item_value(oauth_client_id_vault_item)
189 web_envs['CONCOURSE_GENERIC_OAUTH_CLIENT_ID'] = '${CONCOURSE_GENERIC_OAUTH_CLIENT_ID}'
192 oauth_client_secret = nil
193 oauth_client_secret_vault_item = node['concourse-ci']['docker-compose']['web_oauth_client_secret_vault_item']
194 unless oauth_client_secret_vault_item.empty?
195 oauth_client_secret = get_vault_item_value(oauth_client_secret_vault_item)
196 web_envs['CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET'] = '${CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET}'
199 external_url = web_envs_org['CONCOURSE_EXTERNAL_URL']
200 web_envs['CONCOURSE_EXTERNAL_URL'] = "http://#{node['ipaddress']}:8080" if external_url.nil?
202 data_source = web_envs_org['CONCOURSE_POSTGRES_DATA_SOURCE']
203 # for backward compatibility.
204 data_source = data_source.gsub(/<POSTGRES_PASSWORD>/, '${POSTGRES_PASSWORD}')
205 web_envs['CONCOURSE_POSTGRES_DATA_SOURCE'] = data_source
207 if node['concourse-ci']['docker-compose']['import_ca']
208 ::Chef::Recipe.send(:include, SSLCert::Helper)
209 node['concourse-ci']['ssl_cert']['ca_names'].each {|ca_name|
210 web_vols.push("#{ca_cert_path(ca_name)}:/usr/share/ca-certificates/#{ca_name}.crt:ro")
213 import_ca_script = '/usr/local/bin/concourse_import_ca'
214 template "#{bin_dir}/concourse_import_ca" do
215 source 'opt/docker-compose/app/concourse/bin/concourse_import_ca'
221 web_vols.push("#{bin_dir}/concourse_import_ca:#{import_ca_script}:ro")
223 image_entrypoint = node['concourse-ci']['docker-image']['entrypoint']
224 override_config_srvs['concourse-web']['entrypoint'] \
225 = "/bin/sh -c \"#{import_ca_script} && #{image_entrypoint} web\""
226 if config_format_version == '2'
227 node.rm('concourse-ci', 'docker-compose', 'config', 'services', 'concourse-web', 'command')
229 node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-web', 'command')
233 template "#{bin_dir}/concourse_up" do
234 source 'opt/docker-compose/app/concourse/bin/concourse_up'
241 if node['concourse-ci']['with_ssl_cert_cookbook']
242 ::Chef::Recipe.send(:include, SSLCert::Helper)
243 cn = node['concourse-ci']['ssl_cert']['common_name']
244 # Concourse web process owner is root.
245 web_vols.push("#{server_cert_path(cn)}:/root/server.crt:ro")
246 web_vols.push("#{server_key_path(cn)}:/root/server.key:ro")
247 web_envs['CONCOURSE_TLS_CERT'] = '/root/server.crt'
248 web_envs['CONCOURSE_TLS_KEY'] = '/root/server.key'
251 # merge environment hash
252 force_override_config_srvs['concourse-web']['environment'] = web_envs unless web_envs.empty?
253 # reset vlumes array.
254 override_config_srvs['concourse-web']['volumes'] = web_vols unless web_vols.empty?
257 worker_vols = config_srvs['concourse-worker']['volumes'].to_a
258 worker_vols.push("#{node['concourse-ci']['docker-compose']['worker_keys_dir']}:/concourse-keys")
259 # reset vlumes array.
260 override_config_srvs['concourse-worker']['volumes'] = worker_vols unless worker_vols.empty?
263 source 'opt/docker-compose/app/concourse/.env'
268 # prevent Chef from logging password attribute value.
271 db_passwd: db_passwd,
272 basic_auth_passwd: basic_auth_passwd,
273 oauth_client_id: oauth_client_id,
274 oauth_client_secret: oauth_client_secret
278 template config_file do
279 source 'opt/docker-compose/app/concourse/docker-compose.yml'
286 Note: You must execute the following command manually.
290 $ sudo docker-compose up
292 $ sudo docker-compose down