screwdriver Cookbook
====================

This cookbook sets up a Screwdriver CI/CD service by Docker Compose.

## Contents

- [Requirements](#requirements)
    - [platforms](#platforms)
    - [packages](#packages)
    - [cookbooks](#cookbooks)
- [Attributes](#attributes)
- [Usage](#usage)
    - [Recipes](#recipes)
        - [screwdriver::default](#screwdriverdefault)
        - [screwdriver::docker-compose](#screwdriverdocker-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)
    - [JWT private and public keys management by Chef Vault](#jwt-private-and-public-keys-management-by-chef-vault)
    - [Cookie password management by Chef Vault](#cookie-password-management-by-chef-vault)
    - [Secrets encryption password management by Chef Vault](#secrets-encryption-password-management-by-chef-vault)
    - [OAuth client ID and secret management by Chef Vault](#oauth-client-id-and-secret-management-by-chef-vault)
    - [GitHub webhook secret management by Chef Vault](#github-webhook-secret-management-by-chef-vault)
- [License and Authors](#license-and-authors)

## Requirements

### platforms
- Debian >= 9.0
- Ubuntu >= 14.04
- CentOS, RHEL >= 7.3

### packages
- none.

### cookbooks
- `docker-grid`
- `ssl_cert`

## Attributes

|Key|Type|Description, example|Default|
|:--|:--|:--|:--|
|`['screwdriver']['with_ssl_cert_cookbook']`|Boolean|See `attributes/default.rb`|`false`|
|`['screwdriver']['ssl_cert']['ca_names']`|Array|Internal CA names that are imported by the ssl_cert cookbook.|`[]`|
|`['screwdriver']['ssl_cert']['common_name']`|String|Server common name for TLS|`node['fqdn']`|
|`['screwdriver']['ui']['tls_setup_mode']`|String|`'reverseproxy'` only. Note: [_Add TLS support to UI docker container #377_](https://github.com/screwdriver-cd/screwdriver/issues/377)|`'reverseproxy'`|
|`['screwdriver']['api']['config']`|Hash|This hash object is expanded to a `/config/local.yaml` file in the API Docker container.|See `attributes/default.rb`|
|`['screwdriver']['store']['config']`|Hash|This hash object is expanded to a `/config/local.yaml` file in the Store Docker container.|See `attributes/default.rb`|
|`['screwdriver']['docker-compose']['import_ca']`|Boolean|whether import internal CA certificates or not.|`false`|
|`['screwdriver']['docker-compose']['app_dir']`|String|Path string.|`"#{node['docker-grid']['compose']['app_dir']}/screwdriver"`|
|`['screwdriver']['docker-compose']['bin_dir']`|String|Path string.|`"#{node['screwdriver']['docker-compose']['app_dir']}/bin"`|
|`['screwdriver']['docker-compose']['config_dir']`|String|Path string.|`"#{node['screwdriver']['docker-compose']['app_dir']}/config"`|
|`['screwdriver']['docker-compose']['data_dir']`|String|Path string.|`"#{node['screwdriver']['docker-compose']['app_dir']}/data"`|
|`['screwdriver']['docker-compose']['etc_dir']`|String|Path string.|`"#{node['screwdriver']['docker-compose']['app_dir']}/etc"`|
|`['screwdriver']['docker-compose']['jwt_private_key_reset']`|Boolean|Only available if the JWT key pair is automatically generated by Chef.|`false`|
|`['screwdriver']['docker-compose']['jwt_private_key_vault_item']`|Hash|Optional, Sets a JWT private key from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['jwt_public_key_vault_item']`|Hash|Optional, Sets a JWT public key from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['cookie_password_vault_item']`|Hash|Optional, Sets a session cookie password from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['password_vault_item']`|Hash|Optional, Sets a password for secrets encryption from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['oauth_client_id_vault_item']`|Hash|Required, Sets a OAuth client ID for SCM from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['oauth_client_secret_vault_item']`|Hash|Required, Sets a OAuth secret for SCM from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['webhook_github_secret_vault_item']`|Hash|Required for GitHub, Sets a secret for GitHub webhook from Chef Vault. See `attributes/default.rb`|`{}`|
|`['screwdriver']['docker-compose']['config']`|Hash|`docker-compose.yml` configurations.|See `attributes/default.rb`|

## Usage

### Recipes

#### screwdriver::default

This recipe does nothing.

#### screwdriver::docker-compose

This recipe generates JWT key pair and a `docker-compose.yml` file for the Screwdriver CI/CD service.

### Role Examples

- `roles/screwdriver.rb`

```ruby
name 'screwdriver'
description 'screwdriver'

ui_port     = '9000'
api_port    = '9001'
store_port  = '9002'

run_list(
  'role[docker]',
  'recipe[screwdriver::docker-compose]',
)

override_attributes(
  'screwdriver' => {
    'docker-compose' => {
      'oauth_client_id_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'oauth_client_id',
        'env_context' => false,
        'key' => 'cid',  # real hash path: "/cid"
      },
      'oauth_client_secret_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'oauth_client_secret',
        'env_context' => false,
        'key' => 'secret',  # real hash path: "/secret"
      },
      'config' => {
        'services' => {
          'api' => {
            'ports' => [
              "#{api_port}:80",
            ],
            'environment' => {
              'SCM_PLUGIN' => 'gitlab',  # 'gitlab' or 'github' or 'bitbucket'
              # OAuth Callback URL: "http://#{node['fqdn']}:9001/v4/auth/login/web"
              'SCM_USERNAME' => 'ci-tool',
              'SCM_EMAIL' => 'citool@mail.example.com',
              #'WEBHOOK_GITHUB_SECRET' => 'SUPER-SECRET-SIGNING-THING',
              'SCM_GITLAB_HOST' => 'gitlab.io.example.com',
              'SCM_GITLAB_PROTOCOL' => 'https',
              'NODE_TLS_REJECT_UNAUTHORIZED' => '0',  # for self-signed cetificates
              # The following variables will be set by the screwdriver::docker-compose recipe automatically.
              #'SECRET_OAUTH_CLIENT_ID' => '${SECRET_OAUTH_CLIENT_ID}',
              #'SECRET_OAUTH_CLIENT_SECRET' => '${SECRET_OAUTH_CLIENT_SECRET}',
              #'SECRET_JWT_PRIVATE_KEY' => '${SECRET_JWT_PRIVATE_KEY}',
              #'SECRET_JWT_PUBLIC_KEY' => '${SECRET_JWT_PUBLIC_KEY}',
              #'ECOSYSTEM_UI' => "http://#{node['fqdn']}:#{ui_port}",
              #'ECOSYSTEM_STORE' => "http://#{node['fqdn']}:#{store_port}",
            },
          },
          'ui' => {
            'ports' => [
              "#{ui_port}:80",
            ],
            'environment' => {
              # These variables will be set by the screwdriver::docker-compose recipe automatically.
              #'ECOSYSTEM_API' => "http://#{node['fqdn']}:#{api_port}",
              #'ECOSYSTEM_STORE' => "http://#{node['fqdn']}:#{store_port}",
            },
          },
          'store' => {
            'ports' => [
              "#{store_port}:80",
            ],
            'environment' => {
              # These variables will be set by the screwdriver::docker-compose recipe automatically.
              #'ECOSYSTEM_UI' => "http://#{node['fqdn']}:#{ui_port}",
              #'SECRET_JWT_PUBLIC_KEY' => '${SECRET_JWT_PUBLIC_KEY}',
            },
          },
        },
      },
    },
  },
)
```

- `roles/screwdriver-with-ssl.rb`

```ruby
name 'screwdriver-with-ssl'
description 'screwdriver with SSL'

cn = 'screwdriver.io.example.com'
ui_port     = '9000'
api_port    = '9001'
store_port  = '9002'

run_list(
  'role[docker]',
  'recipe[screwdriver::docker-compose]',
)

override_attributes(
  'ssl_cert' => {
    #'common_names' => [
    #  cn,  # screwdriver cookbook < 0.2.2
    #],
  },
  'screwdriver' => {
    'with_ssl_cert_cookbook' => true,
    'ssl_cert' => {
      'common_name' => cn,
    },
    'docker-compose' => {
      'oauth_client_id_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'oauth_client_id',
        'env_context' => false,
        'key' => 'cid',  # real hash path: "/cid"
      },
      'oauth_client_secret_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'oauth_client_secret',
        'env_context' => false,
        'key' => 'secret',  # real hash path: "/secret"
      },
      'config' => {
        'services' => {
          'reverseproxy' => {
            'ports' => [
              "#{ui_port}:9000",
            ],
            'environment' => {
            },
          },
          'api' => {
            'ports' => [
              "#{api_port}:80",
            ],
            'environment' => {
              'SCM_PLUGIN' => 'gitlab',  # 'gitlab' or 'github' or 'bitbucket'
              # OAuth Callback URL: "http://#{node['fqdn']}:9001/v4/auth/login/web"
              'SCM_USERNAME' => 'ci-tool',
              'SCM_EMAIL' => 'citool@mail.example.com',
              #'WEBHOOK_GITHUB_SECRET' => 'SUPER-SECRET-SIGNING-THING',
              'SCM_GITLAB_HOST' => 'gitlab.io.example.com',
              'SCM_GITLAB_PROTOCOL' => 'https',
              'NODE_TLS_REJECT_UNAUTHORIZED' => '0',  # for self-signed cetificates
              # The following variables will be set by the screwdriver::docker-compose recipe automatically.
              #'SECRET_OAUTH_CLIENT_ID' => '${SECRET_OAUTH_CLIENT_ID}',
              #'SECRET_OAUTH_CLIENT_SECRET' => '${SECRET_OAUTH_CLIENT_SECRET}',
              #'SECRET_JWT_PRIVATE_KEY' => '${SECRET_JWT_PRIVATE_KEY}',
              #'SECRET_JWT_PUBLIC_KEY' => '${SECRET_JWT_PUBLIC_KEY}',
              #'ECOSYSTEM_UI' => "http://#{node['fqdn']}:#{ui_port}",
              #'ECOSYSTEM_STORE' => "http://#{node['fqdn']}:#{store_port}",
            },
          },
          'ui' => {
            #'ports' => [
            #  "#{ui_port}:80",
            #],
            'environment' => {
              # These variables will be set by the screwdriver::docker-compose recipe automatically.
              #'ECOSYSTEM_API' => "http://#{node['fqdn']}:#{api_port}",
              #'ECOSYSTEM_STORE' => "http://#{node['fqdn']}:#{store_port}",
            },
          },
          'store' => {
            'ports' => [
              "#{store_port}:80",
            ],
            'environment' => {
              # These variables will be set by the screwdriver::docker-compose recipe automatically.
              #'ECOSYSTEM_UI' => "http://#{node['fqdn']}:#{ui_port}",
              #'SECRET_JWT_PUBLIC_KEY' => '${SECRET_JWT_PUBLIC_KEY}',
            },
          },
        },
      },
    },
  },
)
```

### SSL server keys and certificates management by ssl_cert cookbook

- create vault items.

```text
$ ruby -rjson -e 'puts JSON.generate({"private" => File.read("screwdriver.io.example.com.prod.key")})' \
> > ~/tmp/screwdriver.io.example.com.prod.key.json

$ ruby -rjson -e 'puts JSON.generate({"public" => File.read("screwdriver.io.example.com.prod.crt")})' \
> > ~/tmp/screwdriver.io.example.com.prod.crt.json

$ cd $CHEF_REPO_PATH

$ knife vault create ssl_server_keys screwdriver.io.example.com.prod \
> --json ~/tmp/screwdriver.io.example.com.prod.key.json

$ knife vault create ssl_server_certs screwdriver.io.example.com.prod \
> --json ~/tmp/screwdriver.io.example.com.prod.crt.json
```

- grant reference permission to the screwdriver host

```text
$ knife vault update ssl_server_keys  screwdriver.io.example.com.prod -S 'name:screwdriver-host.example.com'
$ knife vault update ssl_server_certs screwdriver.io.example.com.prod -S 'name:screwdriver-host.example.com'
```

- modify attributes

```ruby
override_attributes(
  'ssl_cert' => {
    #'common_names' => [
    #  'screwdriver.io.example.com',  # screwdriver cookbook < 0.2.2
    #],
  },
  'screwdriver' => {
    'with_ssl_cert_cookbook' => true,
    'ssl_cert' => {
      'common_name' => 'screwdriver.io.example.com',
    },
    # ...
  },
)
```

### JWT private and public keys management by Chef Vault

- create vault items.

```text
$ ruby -rjson -e 'puts JSON.generate({"private" => File.read("screwdriver_jwt_private.key")})' \
> > ~/sec/tmp/screwdriver_jwt_private.key.json

$ ruby -rjson -e 'puts JSON.generate({"public" => File.read("screwdriver_jwt_public.key")})' \
> > ~/sec/tmp/screwdriver_jwt_public.key.json

$ cd $CHEF_REPO_PATH

$ knife vault create screwdriver jwt_private_key \
> --json ~/sec/tmp/screwdriver_jwt_private.key.json

$ knife vault create screwdriver screwdriver_jwt_public \
> --json ~/sec/tmp/screwdriver_jwt_public.key.json
```

- grant reference permission to the screwdriver host

```text
$ knife vault update screwdriver jwt_private_key -S 'name:screwdriver-host.example.com'
$ knife vault update screwdriver jwt_public_key  -S 'name:screwdriver-host.example.com'
```

- modify attributes

```ruby
override_attributes(
  'screwdriver' => {
    # ...
    'docker-compose' => {
      'jwt_private_key_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'jwt_private_key',
        'env_context' => false,
        'key' => 'private',
      },
      'jwt_public_key_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'jwt_public_key',
        'env_context' => false,
        'key' => 'public',
      },
      # ...
    },
  },
)
```

### Cookie password management by Chef Vault

- create vault items.

```text
# A password used for encrypting session data. Needs to be minimum 32 characters
$ cat ~/tmp/screwdriver_cookie_password.json
{"password":"********************************"}

$ cd $CHEF_REPO_PATH
$ knife vault create screwdriver cookie_password --json ~/sec/tmp/screwdriver_cookie_password.json
```

- grant reference permission to the screwdriver host

```text
$ knife vault update screwdriver cookie_password -S 'name:screwdriver-host.example.com'
```

- modify attributes

```ruby
override_attributes(
  'screwdriver' => {
    # ...
    'docker-compose' => {
      'cookie_password_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'cookie_password',
        'env_context' => false,
        'key' => 'password',
      },
      # ...
    },
  },
)
```

### Secrets encryption password management by Chef Vault

- create vault items.

```text
# A password used for encrypting stored secrets. Needs to be minimum 32 characters
$ cat ~/tmp/screwdriver_password.json
{"password":"********************************"}

$ cd $CHEF_REPO_PATH
$ knife vault create screwdriver password --json ~/sec/tmp/screwdriver_password.json
```

- grant reference permission to the screwdriver host

```text
$ knife vault update screwdriver password -S 'name:screwdriver-host.example.com'
```

- modify attributes

```ruby
override_attributes(
  'screwdriver' => {
    # ...
    'docker-compose' => {
      'password_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'password',
        'env_context' => false,
        'key' => 'password',
      },
      # ...
    },
  },
)
```

### OAuth client ID and secret management by Chef Vault

- create vault items.

```text
$ cat ~/sec/tmp/screwdriver_oauth_client_id.json
{"cid":"***************************************************************"}
$ cat ~/sec/tmp/screwdriver_oauth_client_secret.json
{"secret":"***************************************************************"}

$ cd $CHEF_REPO_PATH

$ knife vault create screwdriver oauth_client_id --json ~/sec/tmp/screwdriver_oauth_client_id.json
$ knife vault create screwdriver oauth_client_secret --json ~/sec/tmp/screwdriver_oauth_client_secret.json
```

- grant reference permission to the screwdriver host

```text
$ knife vault update screwdriver oauth_client_id -S 'name:screwdriver-host.example.com'
$ knife vault update screwdriver oauth_client_secret -S 'name:screwdriver-host.example.com'
```

- modify attributes

```ruby
override_attributes(
  'screwdriver' => {
    # ...
    'docker-compose' => {
      'oauth_client_id_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'oauth_client_id',
        'env_context' => false,
        'key' => 'cid',
      },
      'oauth_client_secret_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'oauth_client_secret',
        'env_context' => false,
        'key' => 'secret',
      },
      # ...
    },
  },
)
```

### GitHub webhook secret management by Chef Vault

- create vault items.

```text
$ cat ~/tmp/screwdriver_webhook_github_secret.json
{"secret":"********************************"}

$ cd $CHEF_REPO_PATH
$ knife vault create screwdriver webhook_github_secret --json ~/sec/tmp/screwdriver_webhook_github_secret.json
```

- grant reference permission to the screwdriver host

```text
$ knife vault update screwdriver webhook_github_secret -S 'name:screwdriver-host.example.com'
```

- modify attributes

```ruby
override_attributes(
  'screwdriver' => {
    # ...
    'docker-compose' => {
      'webhook_github_secret_vault_item' => {
        'vault' => 'screwdriver',
        'name' => 'webhook_github_secret',
        'env_context' => false,
        'key' => 'secret',
      },
      # ...
    },
  },
)
```

## License and Authors

- Author:: whitestar at osdn.jp

```text
Copyright 2017, whitestar

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
