OSDN Git Service

improves CA certificates import feature.
[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 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)
163 else
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']
176       else
177         # 5. auto generate.
178         SecureRandom.hex  # or urlsafe_base64
179       end
180   end
181 end
182 # prevent Chef from logging password attribute value. (=> template variables)
183 web_envs['CONCOURSE_BASIC_AUTH_PASSWORD'] = '${CONCOURSE_BASIC_AUTH_PASSWORD}'
184
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}'
190 end
191
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}'
197 end
198
199 external_url = web_envs_org['CONCOURSE_EXTERNAL_URL']
200 web_envs['CONCOURSE_EXTERNAL_URL'] = "http://#{node['ipaddress']}:8080" if external_url.nil?
201
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
206
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")
211   }
212
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'
216     owner 'root'
217     group 'root'
218     mode '0755'
219     action :create
220   end
221   web_vols.push("#{bin_dir}/concourse_import_ca:#{import_ca_script}:ro")
222
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')
228   else
229     node.rm('concourse-ci', 'docker-compose', 'config', 'concourse-web', 'command')
230   end
231 end
232
233 template "#{bin_dir}/concourse_up" do
234   source 'opt/docker-compose/app/concourse/bin/concourse_up'
235   owner 'root'
236   group 'root'
237   mode '0755'
238   action :create
239 end
240
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'
249 end
250
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?
255
256 # Worker
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?
261
262 template env_file do
263   source 'opt/docker-compose/app/concourse/.env'
264   owner 'root'
265   group 'root'
266   mode '0600'
267   sensitive true
268   # prevent Chef from logging password attribute value.
269   variables(
270     # secrets
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
275   )
276 end
277
278 template config_file do
279   source  'opt/docker-compose/app/concourse/docker-compose.yml'
280   owner 'root'
281   group 'root'
282   mode '0600'
283 end
284
285 log <<-"EOM"
286 Note: You must execute the following command manually.
287   See #{doc_url}
288   - Start:
289     $ cd #{app_dir}
290     $ sudo docker-compose up
291   - Stop
292     $ sudo docker-compose down
293 EOM