concourse-ci CHANGELOG
======================
+0.1.6
+-----
+- adds CA certificates import feature.
+- adds the `['concourse-ci']['docker-compose']['web_oauth_client_(id|secret)_vault_item']` attributes.
+
0.1.5
-----
- adds the `concourse-ci::fly` recipe.
- [concourse-ci::docker-compose](#concourse-cidocker-compose)
- [Role Examples](#role-examples)
- [SSL server keys and certificates management by ssl_cert cookbook](#ssl-server-keys-and-certificates-management-by-ssl_cert-cookbook)
+ - [OAuth client ID and secret management by Chef Vault](#oauth-client-id-and-secret-management-by-chef-vault)
- [License and Authors](#license-and-authors)
## Requirements
|`['concourse-ci']['fly']['release_checksum']`|String||`nil`|
|`['concourse-ci']['fly']['auto_upgrade']`|Boolean||`false`|
|`['concourse-ci']['fly']['install_path']`|String||`'/usr/local/bin/fly'`|
+|`['concourse-ci']['with_ssl_cert_cookbook']`|Boolean|See `attributes/default.rb`|`false`|
+|`['concourse-ci']['ssl_cert']['ca_names']`|Array|Internal CA names that are imported by the ssl_cert cookbook.|`[]`|
+|`['concourse-ci']['ssl_cert']['common_name']`|String|Server common name for TLS|`node['fqdn']`|
+|`['concourse-ci']['docker-compose']['import_ca']`|Boolean|whether import internal CA certificates or not.|`false`|
|`['concourse-ci']['docker-compose']['app_dir']`|String||`"#{node['docker-grid']['compose']['app_dir']}/concourse"`|
|`['concourse-ci']['docker-compose']['pgdata_dir']`|String|Path string or nil (unset).|`"#{node['concourse-ci']['docker-compose']['app_dir']}/database"`|
|`['concourse-ci']['docker-compose']['web_keys_dir']`|String|Path string.|`"#{node['concourse-ci']['docker-compose']['app_dir']}/keys/web"`|
|`['concourse-ci']['docker-compose']['db_password_vault_item']`|Hash|See `attributes/default.rb`|`{}`|
|`['concourse-ci']['docker-compose']['web_password_reset']`|String|Only available if the password is automatically generated by Chef.|`false`|
|`['concourse-ci']['docker-compose']['web_password_vault_item']`|Hash|See `attributes/default.rb`|`{}`|
+|`['concourse-ci']['docker-compose']['web_oauth_client_id_vault_item']`|Hash|See `attributes/default.rb`|`{}`|
+|`['concourse-ci']['docker-compose']['web_oauth_client_secret_vault_item']`|Hash|See `attributes/default.rb`|`{}`|
|`['concourse-ci']['docker-compose']['ssh_keys_reset']`|String|Resets all SSH keys forcely.|`false`|
|`['concourse-ci']['docker-compose']['config_format_version']`|String|Read only. `docker-compose.yml` format version. Only version 1 is supported now.|`'1'`|
|`['concourse-ci']['docker-compose']['config']`|Hash|`docker-compose.yml` configurations.|See `attributes/default.rb`|
)
```
+- `roles/concourse-with-oauth.rb`
+
+```ruby
+name 'concourse-with-oauth'
+description 'Concourse with OAuth'
+
+run_list(
+ 'recipe[ssl_cert::ca_certs]',
+ 'recipe[ssl_cert::server_key_pairs]',
+ 'role[docker]',
+ 'recipe[concourse-ci::docker-compose]',
+)
+
+image = 'concourse/concourse:2.7.0'
+port = '18443'
+ca_name = 'grid_ca'
+cn = 'concourse.io.example.com'
+gitlab_cn = 'gitlab.io.example.com'
+
+override_attributes(
+ 'ssl_cert' => {
+ 'ca_names' => [
+ ca_name,
+ ],
+ 'common_names' => [
+ cn,
+ ],
+ },
+ 'concourse-ci' => {
+ 'with_ssl_cert_cookbook' => true,
+ 'ssl_cert' => {
+ 'ca_names' => [
+ ca_name,
+ ],
+ 'common_name' => cn,
+ },
+ 'docker-compose' => {
+ 'import_ca' => true,
+ 'web_oauth_client_id_vault_item' => {
+ 'vault' => 'concourse',
+ 'name' => 'web_oauth_client_id',
+ 'env_context' => false,
+ 'key' => 'cid',
+ },
+ 'web_oauth_client_secret_vault_item' => {
+ 'vault' => 'concourse',
+ 'name' => 'web_oauth_client_secret',
+ 'env_context' => false,
+ 'key' => 'secret',
+ },
+ 'config' => {
+ # Version 1 docker-compose format
+ 'concourse-web' => {
+ 'ports' => [
+ #'4080:8080',
+ "#{port}:8443",
+ ],
+ 'environment' => {
+ 'CONCOURSE_TLS_BIND_PORT' => '8443',
+ 'CONCOURSE_EXTERNAL_URL' => "https://#{cn}:#{port}",
+ # OAuth for the default `main`` team
+ 'CONCOURSE_GENERIC_OAUTH_DISPLAY_NAME' => 'GitLab',
+ # The following 2 variables are set automatically,
+ # if the ['concourse-ci']['docker-compose']['web_oauth_client_(id|secret)_vault_item'] attributes are specified.
+ #'CONCOURSE_GENERIC_OAUTH_CLIENT_ID' => '${CONCOURSE_GENERIC_OAUTH_CLIENT_ID}',
+ #'CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET' => '${CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET}',
+ 'CONCOURSE_GENERIC_OAUTH_AUTH_URL' => "https://#{gitlab_cn}/oauth/authorize",
+ 'CONCOURSE_GENERIC_OAUTH_TOKEN_URL' => "https://#{gitlab_cn}/oauth/token",
+ },
+ },
+ },
+ },
+ },
+)
+```
+
### SSL server keys and certificates management by ssl_cert cookbook
- create vault items.
$ ruby -rjson -e 'puts JSON.generate({"private" => File.read("concourse_io_example_com.prod.key")})' \
> > ~/tmp/concourse_io_example_com.prod.key.json
-$ knife vault create ssl_server_keys concourse.io.example.com.prod \
-> --json ~/tmp/concourse_io_example_com.prod.key.json
-
$ ruby -rjson -e 'puts JSON.generate({"public" => File.read("concourse_io_example_com.prod.crt")})' \
> > ~/tmp/concourse_io_example_com.prod.crt.json
+$ cd $CHEF_REPO_PATH
+
+$ knife vault create ssl_server_keys concourse.io.example.com.prod \
+> --json ~/tmp/concourse_io_example_com.prod.key.json
+
$ knife vault create ssl_server_certs concourse.io.example.com.prod \
> --json ~/tmp/concourse_io_example_com.prod.crt.json
```
)
```
+### OAuth client ID and secret management by Chef Vault
+
+- create vault items.
+
+```text
+$ cat ~/tmp/concourse_oauth_client_id.json
+{"cid":"***************************************************************"}
+$ cat ~/tmp/concourse_oauth_client_secret.json
+{"secret":"***************************************************************"}
+
+$ knife vault create concourse web_oauth_client_id --json ~/tmp/concourse_oauth_client_id.json
+$ knife vault create concourse web_oauth_client_secret --json ~/tmp/concourse_oauth_client_secret.json
+```
+
+- grant reference permission to the Concourse host
+
+```text
+$ knife vault update concourse web_oauth_client_id -S 'name:concourse-host.example.com'
+$ knife vault update concourse web_oauth_client_secret -S 'name:concourse-host.example.com'
+```
+
+- modify attributes
+
+```ruby
+override_attributes(
+ 'concourse-ci' => {
+ # ...
+ 'docker-compose' => {
+ 'web_oauth_client_id_vault_item' => {
+ 'vault' => 'concourse',
+ 'name' => 'web_oauth_client_id',
+ 'env_context' => false,
+ 'key' => 'cid',
+ },
+ 'web_oauth_client_secret_vault_item' => {
+ 'vault' => 'concourse',
+ 'name' => 'web_oauth_client_secret',
+ 'env_context' => false,
+ 'key' => 'secret',
+ },
+ # ...
+ },
+ },
+)
+```
+
## License and Authors
- Author:: whitestar at osdn.jp
# If ['concourse-ci']['with_ssl_cert_cookbook'] is true,
# node['concourse-ci']['docker-compose']['config']
# are overridden by the following 'common_name' attributes.
+default['concourse-ci']['ssl_cert']['ca_names'] = []
default['concourse-ci']['ssl_cert']['common_name'] = node['fqdn']
+default['concourse-ci']['docker-compose']['import_ca'] = false
default['concourse-ci']['docker-compose']['app_dir'] = "#{node['docker-grid']['compose']['app_dir']}/concourse"
default['concourse-ci']['docker-compose']['pgdata_dir'] = "#{node['concourse-ci']['docker-compose']['app_dir']}/database"
default['concourse-ci']['docker-compose']['web_keys_dir'] = "#{node['concourse-ci']['docker-compose']['app_dir']}/keys/web"
#'key' => 'hash/path/to/password', # real hash path: "/#{node.chef_environment}/hash/path/to/password"
=end
}
+default['concourse-ci']['docker-compose']['web_oauth_client_id_vault_item'] = {
+=begin
+ 'vault' => 'concourse',
+ 'name' => 'web_oauth_client_id',
+ # single cid or nested hash cid path delimited by slash
+ 'env_context' => false,
+ 'key' => 'cid', # real hash path: "/cid", Note: do not use `id`, which is preserved by Chef Vault.
+ # or nested hash id path delimited by slash
+ #'env_context' => true,
+ #'key' => 'hash/path/to/cid', # real hash path: "/#{node.chef_environment}/hash/path/to/cid"
+=end
+}
+default['concourse-ci']['docker-compose']['web_oauth_client_secret_vault_item'] = {
+=begin
+ 'vault' => 'concourse',
+ 'name' => 'web_oauth_client_secret',
+ # single secret or nested hash secret path delimited by slash
+ 'env_context' => false,
+ 'key' => 'secret', # real hash path: "/secret"
+ # or nested hash secret path delimited by slash
+ #'env_context' => true,
+ #'key' => 'hash/path/to/secret', # real hash path: "/#{node.chef_environment}/hash/path/to/secret"
+=end
+}
default['concourse-ci']['docker-compose']['ssh_keys_reset'] = false
# TODO: support version 2 format, and use `default` instead of `force_override`
'CONCOURSE_BASIC_AUTH_USERNAME' => 'concourse',
# Note: You should use the `['concourse-ci']['docker-compose']['web_password_vault_item']` attribute.
'CONCOURSE_BASIC_AUTH_PASSWORD' => nil,
+ # OAuth for the default `main`` team
+ #'CONCOURSE_GENERIC_OAUTH_DISPLAY_NAME' => 'GitLab',
+ # The following 2 variables are set automatically,
+ # if the ['concourse-ci']['docker-compose']['web_oauth_client_(id|secret)_vault_item'] attributes are specified.
+ #'CONCOURSE_GENERIC_OAUTH_CLIENT_ID' => '${CONCOURSE_GENERIC_OAUTH_CLIENT_ID}',
+ #'CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET' => '${CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET}',
+ #'CONCOURSE_GENERIC_OAUTH_AUTH_URL' => 'https://gitlab.io.example.com/oauth/authorize',
+ #'CONCOURSE_GENERIC_OAUTH_TOKEN_URL' => 'https://gitlab.io.example.com/oauth/token',
# If you sepecify no value, Chef will sets "http://#{node['ipaddress']}:8080".
'CONCOURSE_EXTERNAL_URL' => nil,
# `${POSTGRES_PASSWORD}` is a placeholder of password string.
license 'Apache 2.0'
description 'Installs/Configures Concourse CI by Docker Compose'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
-version '0.1.5'
+version '0.1.6'
source_url 'http://scm.osdn.jp/gitroot/metasearch/grid-chef-repo.git'
issues_url 'https://osdn.jp/projects/metasearch/ticket'
include_recipe 'docker-grid::compose'
app_dir = node['concourse-ci']['docker-compose']['app_dir']
+bin_dir = "#{app_dir}/bin"
pgdata_dir = node['concourse-ci']['docker-compose']['pgdata_dir']
web_keys_dir = node['concourse-ci']['docker-compose']['web_keys_dir']
worker_keys_dir = node['concourse-ci']['docker-compose']['worker_keys_dir']
[
app_dir,
+ bin_dir,
web_keys_dir,
worker_keys_dir,
].each {|dir|
# prevent Chef from logging password attribute value. (=> template variables)
web_envs['CONCOURSE_BASIC_AUTH_PASSWORD'] = '${CONCOURSE_BASIC_AUTH_PASSWORD}'
+oauth_client_id = nil
+oauth_client_id_vault_item = node['concourse-ci']['docker-compose']['web_oauth_client_id_vault_item']
+unless oauth_client_id_vault_item.empty?
+ oauth_client_id = get_vault_item_value(oauth_client_id_vault_item)
+ web_envs['CONCOURSE_GENERIC_OAUTH_CLIENT_ID'] = '${CONCOURSE_GENERIC_OAUTH_CLIENT_ID}'
+end
+
+oauth_client_secret = nil
+oauth_client_secret_vault_item = node['concourse-ci']['docker-compose']['web_oauth_client_secret_vault_item']
+unless oauth_client_secret_vault_item.empty?
+ oauth_client_secret = get_vault_item_value(oauth_client_secret_vault_item)
+ web_envs['CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET'] = '${CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET}'
+end
+
external_url = web_envs_org['CONCOURSE_EXTERNAL_URL']
web_envs['CONCOURSE_EXTERNAL_URL'] = "http://#{node['ipaddress']}:8080" if external_url.nil?
data_source = data_source.gsub(/<POSTGRES_PASSWORD>/, '${POSTGRES_PASSWORD}')
web_envs['CONCOURSE_POSTGRES_DATA_SOURCE'] = data_source
+if node['concourse-ci']['docker-compose']['import_ca']
+ ::Chef::Recipe.send(:include, SSLCert::Helper)
+ node['concourse-ci']['ssl_cert']['ca_names'].each {|ca_name|
+ web_vols.push("#{ca_cert_path(ca_name)}:/usr/share/ca-certificates/#{ca_name}.crt:ro")
+ }
+
+ template "#{bin_dir}/concourse_import_ca" do
+ source 'opt/docker-compose/app/concourse/bin/concourse_import_ca'
+ owner 'root'
+ group 'root'
+ mode '0755'
+ action :create
+ end
+ web_vols.push("#{bin_dir}/concourse_import_ca:/usr/local/bin/concourse_import_ca:ro")
+end
+
+template "#{bin_dir}/concourse_up" do
+ source 'opt/docker-compose/app/concourse/bin/concourse_up'
+ owner 'root'
+ group 'root'
+ mode '0755'
+ action :create
+end
+
if node['concourse-ci']['with_ssl_cert_cookbook']
::Chef::Recipe.send(:include, SSLCert::Helper)
cn = node['concourse-ci']['ssl_cert']['common_name']
variables(
# secrets
db_passwd: db_passwd,
- basic_auth_passwd: basic_auth_passwd
+ basic_auth_passwd: basic_auth_passwd,
+ oauth_client_id: oauth_client_id,
+ oauth_client_secret: oauth_client_secret
)
end
See #{doc_url}
- Start:
$ cd #{app_dir}
- $ sudo docker-compose up -d
+ $ ./bin/concourse_up
- Stop
$ sudo docker-compose down
EOM
-%>
POSTGRES_PASSWORD=<%= @db_passwd %>
CONCOURSE_BASIC_AUTH_PASSWORD=<%= @basic_auth_passwd %>
+<% unless @oauth_client_id.nil? %>
+CONCOURSE_GENERIC_OAUTH_CLIENT_ID=<%= @oauth_client_id %>
+<% end %>
+<% unless @oauth_client_secret.nil? %>
+CONCOURSE_GENERIC_OAUTH_CLIENT_SECRET=<%= @oauth_client_secret %>
+<% end %>
--- /dev/null
+#!/bin/sh\r
+\r
+<%\r
+node['concourse-ci']['ssl_cert']['ca_names'].each {|ca_name|\r
+-%>\r
+if ! cat /etc/ca-certificates.conf | grep <%= ca_name %>; then\r
+ echo <%= ca_name %>.crt >> /etc/ca-certificates.conf\r
+fi\r
+<%\r
+}\r
+-%>\r
+update-ca-certificates\r
--- /dev/null
+#!/bin/sh\r
+\r
+cd <%= node['concourse-ci']['docker-compose']['app_dir'] %>\r
+sudo docker-compose up -d\r
+<% if node['concourse-ci']['docker-compose']['import_ca'] %>\r
+sudo docker exec $(sudo docker ps | grep "concourse_concourse-web" | awk '{print $1}') concourse_import_ca\r
+<% end %>\r