stack_car 0.8.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitlab-ci.yml +17 -12
- data/README.md +122 -1
- data/lib/stack_car/cli.rb +253 -41
- data/lib/stack_car/dot_rc.rb +25 -0
- data/lib/stack_car/version.rb +1 -1
- data/lib/stack_car.rb +1 -0
- data/stack_car.gemspec +2 -0
- data/templates/.dockerignore.erb +2 -2
- data/templates/.env.development.erb +2 -0
- data/templates/.env.erb +29 -16
- data/templates/.gitlab/issue_templates/Bug.md +46 -0
- data/templates/.gitlab/issue_templates/Feature.md +41 -0
- data/templates/.gitlab/issue_templates/Question.md +18 -0
- data/templates/.gitlab/merge_request_templates/Bug.md +36 -0
- data/templates/.gitlab/merge_request_templates/Feature.md +36 -0
- data/templates/.gitlab-ci.yml.erb +98 -65
- data/templates/.sops.yaml.erb +3 -0
- data/templates/Dockerfile.erb +26 -7
- data/templates/README.md +81 -7
- data/templates/chart/.gitignore +3 -0
- data/templates/chart/.helmignore +23 -0
- data/templates/chart/Chart.yaml.tt +30 -0
- data/templates/chart/README.md +223 -0
- data/templates/chart/bin/check_sidekiq.rb +0 -0
- data/templates/chart/bin/decrypt +17 -0
- data/templates/chart/bin/deploy +14 -0
- data/templates/chart/bin/encrypt +15 -0
- data/templates/chart/bin/remove +15 -0
- data/templates/chart/sample-values.yaml.tt +153 -0
- data/templates/chart/templates/_helpers.tpl.tt +85 -0
- data/templates/chart/templates/rails-env-cm.yaml.tt +47 -0
- data/templates/chart/templates/rails-env-secret.yaml +10 -0
- data/templates/chart/templates/rails-pvc-shared.yml +20 -0
- data/templates/chart/templates/setup-job.yaml +73 -0
- data/templates/chart/templates/web-deploy.yaml +67 -0
- data/templates/chart/templates/web-ing-wildcard.yaml +20 -0
- data/templates/chart/templates/web-ing.yaml +20 -0
- data/templates/chart/templates/web-svc.yaml +20 -0
- data/templates/chart-fcrepo/fcrepo-deploy.yaml +63 -0
- data/templates/chart-fcrepo/fcrepo-env-cm.yaml +8 -0
- data/templates/chart-fcrepo/fcrepo-env-secret.yaml.tt +10 -0
- data/templates/chart-fcrepo/fcrepo-pvc.yaml +20 -0
- data/templates/chart-fcrepo/fcrepo-svc.yaml +19 -0
- data/templates/chart-sidekiq/sidekiq-deploy.yaml +80 -0
- data/templates/database.yml.erb +10 -11
- data/templates/decrypt-secrets +22 -0
- data/templates/development.rb.erb +90 -0
- data/templates/docker-compose.yml.erb +52 -18
- data/templates/encrypt-secrets +19 -0
- data/templates/env.conf.erb +11 -11
- data/templates/nginx.sh.erb +17 -0
- data/templates/production.rb.erb +117 -0
- metadata +71 -12
- data/templates/Dockerfile.base.erb +0 -48
- data/templates/Dockerfile.builder.erb +0 -13
- data/templates/docker-compose.ci.yml.erb +0 -87
- data/templates/docker-compose.production.yml.erb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 80132883ba506f9cf4414a46f560a2040e8843596b7a94e3d6f9baab6ae3a4d4
|
4
|
+
data.tar.gz: bb84c75a79f699e7bb04aa877eaed36f3acb9e8c8d9bf294d9f4fa7bbe579ddc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e03c8620fcd08038774e75c2cfeefa75a2bae7d42708c0240c774e3584391695a87349c50bba6b0c7b444e46e66d73b2e96b1439bbf09f0104670b83e3fb831
|
7
|
+
data.tar.gz: 3196614e6fd6ce06b7408aa292e4ae0b19ef57aa9df8643899fcf378ad840a2d4d469695010ca04948676b9a46b7b655947cbaa0bf686c59c27b3b385b73341a
|
data/.gitlab-ci.yml
CHANGED
@@ -1,17 +1,22 @@
|
|
1
|
-
|
1
|
+
stages:
|
2
|
+
- rspec
|
2
3
|
|
3
4
|
before_script:
|
4
|
-
bundle
|
5
|
+
- bundle
|
5
6
|
|
6
|
-
pages:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
# pages:
|
8
|
+
# script:
|
9
|
+
# - bundle exec yardoc --plugin yard-thor -o public - templates/.* templates/*
|
10
|
+
# artifacts:
|
11
|
+
# paths:
|
12
|
+
# - public
|
13
|
+
# only:
|
14
|
+
# - master
|
14
15
|
|
15
|
-
|
16
|
+
rspec:
|
17
|
+
stage: rspec
|
18
|
+
image: ruby:2.3
|
16
19
|
script:
|
17
|
-
bundle exec
|
20
|
+
- bundle exec rspec
|
21
|
+
tags:
|
22
|
+
- docker
|
data/README.md
CHANGED
@@ -4,6 +4,13 @@
|
|
4
4
|
|
5
5
|
Stack Car is an opinionated set of tools around Docker and Rails. It provides convenent methods to start and stop docker-compose, to deploy with rancher and a set of templates to get a new Rails app in to docker as quickly as possible.
|
6
6
|
|
7
|
+
## Table of Contents
|
8
|
+
|
9
|
+
- [Installation](#installation)
|
10
|
+
- [Usage](#usage)
|
11
|
+
- [Development](#development)
|
12
|
+
- [Dockerizing an Application](#dockerizing-an-application)
|
13
|
+
- [Generating a Helm Chart](#generating-a-helm-chart)
|
7
14
|
|
8
15
|
## Installation
|
9
16
|
|
@@ -33,7 +40,121 @@ Commands:
|
|
33
40
|
|
34
41
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
35
42
|
|
36
|
-
To install this gem onto your local machine
|
43
|
+
**To install this gem onto your local machine**
|
44
|
+
- Run `bundle exec rake install`
|
45
|
+
|
46
|
+
### Workflow
|
47
|
+
|
48
|
+
**Create a dummy rails app**
|
49
|
+
|
50
|
+
Developing stack_car often requires a rails application for you to run updated commands and templates against. Generate one for this purpose:
|
51
|
+
- `rails new <dummy-app-name>`
|
52
|
+
|
53
|
+
**Make and test your changes**
|
54
|
+
- In stack_car, make your command / template changes
|
55
|
+
- Run `rake install` to update your local gem
|
56
|
+
- In your dummy application, test the updated command
|
57
|
+
- Commit your changes
|
58
|
+
|
59
|
+
### Releasing a new version
|
60
|
+
- Update the version number in `version.rb`
|
61
|
+
- Run `bundle exec rake release`
|
62
|
+
- This will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
63
|
+
|
64
|
+
## Dockerizing an application
|
65
|
+
|
66
|
+
Dockerizing your application with stack_car can be thought of in 2 steps:
|
67
|
+
- **Generate the file templates**
|
68
|
+
- **Customize provided templates to the requirements of the application**
|
69
|
+
|
70
|
+
### Generate templates (`sc dockerize`)
|
71
|
+
You can generate requisite files for running your application for local development in Docker with the **dockerize** command.
|
72
|
+
|
73
|
+
To **dockerize** your application:
|
74
|
+
- `cd` into your project dir
|
75
|
+
- Run `sc dockerize` to generate files, appending **service flags** to scaffold any other services your application requires
|
76
|
+
- **For example**:
|
77
|
+
- For rails/postgres: `sc dockerize --postgres`
|
78
|
+
- For rails/mysql/redis: `sc dockerize --mysql --redis`
|
79
|
+
|
80
|
+
This command will provide:
|
81
|
+
- `Dockerfile`
|
82
|
+
- `docker-compose.yml`
|
83
|
+
- `.env*` files
|
84
|
+
- **ops** files to get you set up for running your application with **nginx**.
|
85
|
+
|
86
|
+
### Customize templates
|
87
|
+
|
88
|
+
stack_car will have provided sensible defaults for your services but customization will be required per needs of each project (ie api tokens and email configuration where applicable).
|
89
|
+
|
90
|
+
**Customization workflow**
|
91
|
+
- Do a text search to find and replace any instances `CHANGEME` in the generated files
|
92
|
+
- Add any **general environment variables** to `.env`
|
93
|
+
- This sets defaults for all docker compose environments
|
94
|
+
- Add any **development environment variables** to `.env.development`
|
95
|
+
- These set up any new values or overrides specific to your development env
|
96
|
+
- Run `sc build` to build your image
|
97
|
+
- On failed build, browse the terminal output to track down and squash any misconfigurations. Rebuild
|
98
|
+
- Upon successful build, run `sc up` to spin up project
|
99
|
+
- If you get errors, browse the terminal output to track down and squash any misconfigurations (refer to the Docker dashboard to see separate logs for each service)
|
100
|
+
- Visit site at `localhost:3000`
|
101
|
+
- Alternatively, visit it at the host you have specified to work with **Dory**
|
102
|
+
- **Note**: *Depending on the DB required by your application, you will need to create the DB. You need to do that within from the container:*
|
103
|
+
- Using the `bundle-exec` command: `sc bundle-exec db:create`
|
104
|
+
- Shelling in and running in the container shell:
|
105
|
+
```bash
|
106
|
+
sc exec bash
|
107
|
+
bundle exec rails db:create
|
108
|
+
```
|
109
|
+
|
110
|
+
Once all services are running and speaking to each other you are good to go.
|
111
|
+
|
112
|
+
**Tips**:
|
113
|
+
- Any changes to `Dockerfile` will require `sc build` for the changes to manifest
|
114
|
+
- Changes to `docker-compose.yml` **do not require rebuild unless you have changed the image**
|
115
|
+
|
116
|
+
## Generating a Helm Chart
|
117
|
+
|
118
|
+
stack_car's **dockerize** command can be used in conjunction with available flags to generate a **Helm chart** template for your application. You will need to create the *values* files with necessary configuration values from the *sample-values* provided by stack_car, but the command will effectively give you the baseline Notch8 template (scripts, template files, template helpers, sample values file) for a **Helm base Kubernetes deploy**
|
119
|
+
|
120
|
+
The following examples are to be run in the repo of the application you are creating the chart for.
|
121
|
+
|
122
|
+
**To generate a Helm chart template**
|
123
|
+
|
124
|
+
- `sc dockerize --helm`
|
125
|
+
- This command without additional flags will only generate Rails web related template files
|
126
|
+
|
127
|
+
In broad strokes adding additional flags signals stack_car to generate template files for other services. Note that any configuration that would normally be applied for these services in a non Helm context (without the `--helm` flag) still apply.
|
128
|
+
|
129
|
+
**For example**:
|
130
|
+
- `sc dockerize --helm --fcrepo --solr`
|
131
|
+
- This command will add templates for the **fcrepo** service and add a **solr chart dependency** in the `Chart.yaml` (You can think of `Chart.yaml` like the **Gemfile** or **package.json** of a Helm chart)
|
132
|
+
|
133
|
+
**Creating values files**
|
134
|
+
|
135
|
+
Values files allows you to configure your helm deploy from number of web instances to hostname for your ingress to environment variables required by your application.
|
136
|
+
|
137
|
+
When starting from a new helm chart, you'll want to copy the sample values file to one named after the environment you're creating a deployment for.
|
138
|
+
|
139
|
+
For example:
|
140
|
+
`cp sample-values.yaml staging-values.yaml`
|
141
|
+
|
142
|
+
*Note: You will do this once for every environment you'd like to deploy*
|
143
|
+
|
144
|
+
**Handling values files**
|
145
|
+
|
146
|
+
Since values files are likely to contain sensitive information like API keys, they should never be committed to your repository. The scripts that stack_car includes in your chart simplifies encrypting and decrypting values for version control.
|
147
|
+
|
148
|
+
Example workflow (given values file is already created):
|
149
|
+
- Edit values file
|
150
|
+
- `bin/encrypt-secrets`
|
151
|
+
- This command will create/update `staging-values.yaml.enc`
|
152
|
+
- Commit and push
|
153
|
+
|
154
|
+
When pulling down a repo or branch, you will need to start by decrypting.
|
155
|
+
|
156
|
+
Example:
|
157
|
+
- `bin/decrypt-secrets`
|
37
158
|
|
38
159
|
## Contributing
|
39
160
|
|
data/lib/stack_car/cli.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'erb'
|
3
3
|
require 'dotenv/load'
|
4
|
-
|
4
|
+
require 'json'
|
5
|
+
require 'byebug'
|
5
6
|
module StackCar
|
6
7
|
class HammerOfTheGods < Thor
|
7
8
|
include Thor::Actions
|
@@ -19,114 +20,180 @@ module StackCar
|
|
19
20
|
method_option :logs, default: true, type: :boolean
|
20
21
|
desc "up", "starts docker-compose with rebuild and orphan removal, defaults to web"
|
21
22
|
def up
|
23
|
+
setup
|
24
|
+
ensure_development_env
|
22
25
|
args = ['--remove-orphans']
|
23
26
|
args << '--build' if options[:build]
|
24
27
|
if options[:build]
|
25
|
-
run("docker-compose pull #{options[:service]}")
|
28
|
+
run("#{dotenv} docker-compose pull #{options[:service]}")
|
26
29
|
end
|
27
30
|
|
28
|
-
run_with_exit("docker-compose up #{args.join(' ')} #{options[:service]}")
|
31
|
+
run_with_exit("#{dotenv} docker-compose up #{args.join(' ')} #{options[:service]}")
|
29
32
|
end
|
30
33
|
|
31
34
|
method_option :service, default: '', type: :string, aliases: '-s'
|
32
35
|
desc "stop", "stops the specified running service, defaults to all"
|
33
36
|
def stop
|
34
|
-
|
37
|
+
setup
|
38
|
+
ensure_development_env
|
39
|
+
run("#{dotenv} docker-compose stop #{options[:service]}")
|
35
40
|
run_with_exit("rm -rf tmp/pids/*")
|
36
41
|
end
|
37
|
-
|
42
|
+
|
43
|
+
method_option :volumes, aliases: '-v'
|
44
|
+
method_option :rmi
|
45
|
+
method_option :'remove-orphans'
|
46
|
+
method_option :service, aliases: '-s'
|
47
|
+
method_option :timeout, aliases: '-t'
|
48
|
+
method_option :all, aliases: '-a'
|
49
|
+
method_option :help, aliases: '-h'
|
50
|
+
desc 'down', 'stops and removes containers and networks specific to this project by default, run with -h for more options'
|
51
|
+
def down
|
52
|
+
setup
|
53
|
+
ensure_development_env
|
54
|
+
|
55
|
+
if options[:help]
|
56
|
+
run('docker-compose down --help')
|
57
|
+
say 'Additional stack_car options:'
|
58
|
+
say ' -a, --all Removes all containers, networks, volumes, and'
|
59
|
+
say ' images created by `up`.'
|
60
|
+
say ' -s, --service Specify a service defined in the Compose file'
|
61
|
+
say ' whose containers and volumes should be removed.'
|
62
|
+
exit(0)
|
63
|
+
end
|
64
|
+
|
65
|
+
if options[:service]
|
66
|
+
rm_vol = true if options[:volumes]
|
67
|
+
|
68
|
+
remove_container(options[:service], rm_vol)
|
69
|
+
exit(0)
|
70
|
+
end
|
71
|
+
|
72
|
+
run_conf = 'Running down will stop and remove all of the Docker containers and networks ' \
|
73
|
+
'defined in the docker-compose.yml file. Continue?'
|
74
|
+
prompt_run_confirmation(run_conf)
|
75
|
+
|
76
|
+
args = []
|
77
|
+
if options[:all]
|
78
|
+
prompt_run_confirmation('--all will remove all containers, volumes, networks, local images, and orphaned containers. Continue?')
|
79
|
+
|
80
|
+
args = %w[--volumes --rmi=local --remove-orphans]
|
81
|
+
else
|
82
|
+
args << '--volumes' if options[:volumes]
|
83
|
+
args << '--rmi=local' if options[:rmi]
|
84
|
+
args << '--remove-orphans' if options[:'remove-orphans']
|
85
|
+
args << '--timeout' if options[:timeout]
|
86
|
+
end
|
87
|
+
|
88
|
+
run("#{dotenv} docker-compose down #{args.join(' ')}")
|
89
|
+
run_with_exit('rm -rf tmp/pids/*')
|
90
|
+
end
|
38
91
|
|
39
92
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
40
93
|
desc "build", "builds specified service, defaults to web"
|
41
94
|
def build
|
42
|
-
|
95
|
+
setup
|
96
|
+
ensure_development_env
|
97
|
+
run_with_exit("#{dotenv} docker-compose build #{options[:service]}")
|
43
98
|
end
|
44
99
|
|
45
100
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
46
101
|
desc "push ARGS", "wraps docker-compose push web unless --service is used to specify"
|
47
102
|
def push(*args)
|
48
|
-
|
103
|
+
setup
|
104
|
+
run_with_exit("#{dotenv} docker-compose push #{options[:service]} #{args.join(' ')}")
|
49
105
|
end
|
50
106
|
|
51
107
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
52
108
|
desc "pull ARGS", "wraps docker-compose pull web unless --service is used to specify"
|
53
109
|
def pull(*args)
|
54
|
-
|
110
|
+
setup
|
111
|
+
run_with_exit("#{dotenv} docker-compose pull #{options[:service]} #{args.join(' ')}")
|
55
112
|
end
|
56
113
|
|
57
114
|
method_option :service, default: '', type: :string, aliases: '-s'
|
58
115
|
desc "ps ARGS", "wraps docker-compose pull web unless --service is used to specify"
|
59
116
|
def ps(*args)
|
60
|
-
|
117
|
+
setup
|
118
|
+
run_with_exit("#{dotenv} docker-compose ps #{options[:service]} #{args.join(' ')}")
|
61
119
|
end
|
62
120
|
map status: :ps
|
63
121
|
|
64
122
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
65
123
|
desc "bundle ARGS", "wraps docker-compose run web unless --service is used to specify"
|
66
124
|
def bundle(*args)
|
67
|
-
|
125
|
+
setup
|
126
|
+
run_with_exit("#{dotenv} docker-compose exec #{options[:service]} bundle")
|
68
127
|
end
|
69
128
|
|
70
129
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
71
130
|
desc "walk ARGS", "wraps docker-compose run web unless --service is used to specify"
|
72
131
|
def walk(*args)
|
73
|
-
|
132
|
+
setup
|
133
|
+
run_with_exit("#{dotenv} docker-compose run #{options[:service]} #{args.join(' ')}")
|
74
134
|
end
|
75
135
|
|
76
136
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
77
137
|
desc "exec ARGS", "wraps docker-compose exec web unless --service is used to specify"
|
78
138
|
def exec(*args)
|
79
|
-
|
139
|
+
setup
|
140
|
+
run_with_exit("#{dotenv} docker-compose exec #{options[:service]} #{args.join(' ')}")
|
80
141
|
end
|
81
142
|
map ex: :exec
|
82
143
|
|
83
144
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
84
145
|
desc 'sh ARGS', "launch a shell using docker-compose exec, sets tty properly"
|
85
146
|
def sh(*args)
|
86
|
-
|
147
|
+
setup
|
148
|
+
run_with_exit("#{dotenv} docker-compose exec -e COLUMNS=\"\`tput cols\`\" -e LINES=\"\`tput lines\`\" #{options[:service]} bash #{args.join(' ')}")
|
87
149
|
end
|
88
150
|
|
89
151
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
90
152
|
desc "bundle_exec ARGS", "wraps docker-compose exec web bundle exec unless --service is used to specify"
|
91
153
|
def bundle_exec(*args)
|
92
|
-
|
154
|
+
setup
|
155
|
+
run_with_exit("#{dotenv} docker-compose exec #{options[:service]} bundle exec #{args.join(' ')}")
|
93
156
|
end
|
94
157
|
map be: :bundle_exec
|
95
158
|
|
96
159
|
method_option :service, default: 'web', type: :string, aliases: '-s'
|
97
160
|
desc "console ARGS", "shortcut to start rails console"
|
98
161
|
def console(*args)
|
99
|
-
|
162
|
+
setup
|
163
|
+
run_with_exit("#{dotenv} docker-compose exec #{options[:service]} bundle exec rails console #{args.join(' ')}")
|
100
164
|
end
|
101
165
|
map rc: :console
|
102
166
|
|
103
167
|
desc "release ENVIRONTMENT", "tag and push and image to the registry"
|
104
168
|
def release(environment)
|
169
|
+
setup
|
105
170
|
timestamp = Time.now.strftime("%Y%m%d%I%M%S")
|
106
171
|
sha = `git rev-parse HEAD`[0..8]
|
107
172
|
registry = "#{ENV['REGISTRY_HOST']}#{ENV['REGISTRY_URI']}"
|
108
173
|
tag = ENV["TAG"] || 'latest'
|
109
174
|
unless File.exists?("#{ENV['HOME']}/.docker/config.json") && File.readlines("#{ENV['HOME']}/.docker/config.json").grep(/#{ENV['REGISTRY_HOST']}/).size > 0
|
110
|
-
run_with_exit("docker login #{ENV['REGISTRY_HOST']}")
|
175
|
+
run_with_exit("#{dotenv(environment)} docker login #{ENV['REGISTRY_HOST']}")
|
111
176
|
end
|
112
|
-
run_with_exit("docker tag #{registry}:#{tag} #{registry}:#{environment}-#{timestamp}")
|
113
|
-
run_with_exit("docker push #{registry}:#{environment}-#{timestamp}")
|
114
|
-
run_with_exit("docker tag #{registry}:#{tag} #{registry}:#{sha}")
|
115
|
-
run_with_exit("docker push #{registry}:#{sha}")
|
116
|
-
run_with_exit("docker tag #{registry}:#{tag} #{registry}:#{environment}-latest")
|
117
|
-
run_with_exit("docker push #{registry}:#{environment}-latest")
|
118
|
-
run_with_exit("docker tag #{registry}:#{tag} #{registry}:latest")
|
119
|
-
run_with_exit("docker push #{registry}:latest")
|
177
|
+
run_with_exit("#{dotenv(environment)} docker tag #{registry}:#{tag} #{registry}:#{environment}-#{timestamp}")
|
178
|
+
run_with_exit("#{dotenv(environment)} docker push #{registry}:#{environment}-#{timestamp}")
|
179
|
+
run_with_exit("#{dotenv(environment)} docker tag #{registry}:#{tag} #{registry}:#{sha}")
|
180
|
+
run_with_exit("#{dotenv(environment)} docker push #{registry}:#{sha}")
|
181
|
+
run_with_exit("#{dotenv(environment)} docker tag #{registry}:#{tag} #{registry}:#{environment}-latest")
|
182
|
+
run_with_exit("#{dotenv(environment)} docker push #{registry}:#{environment}-latest")
|
183
|
+
run_with_exit("#{dotenv(environment)} docker tag #{registry}:#{tag} #{registry}:latest")
|
184
|
+
run_with_exit("#{dotenv(environment)} docker push #{registry}:latest")
|
120
185
|
end
|
121
186
|
|
122
187
|
desc "provision ENVIRONMENT", "configure the servers for docker and then deploy an image"
|
123
188
|
def provision(environment)
|
189
|
+
setup
|
124
190
|
# TODO make dotenv load a specific environment?
|
125
|
-
run_with_exit("DEPLOY_ENV=#{environment} dotenv ansible-playbook -i ops/hosts -l #{environment}:localhost ops/provision.yml")
|
191
|
+
run_with_exit("DEPLOY_ENV=#{environment} #{dotenv(environment)} ansible-playbook -i ops/hosts -l #{environment}:localhost ops/provision.yml")
|
126
192
|
end
|
127
193
|
|
128
194
|
desc "ssh ENVIRONMENT", "log in to a running instance - requires PRODUCTION_SSH to be set"
|
129
195
|
def ssh(environment)
|
196
|
+
setup
|
130
197
|
target = ENV["#{environment.upcase}_SSH"]
|
131
198
|
if target
|
132
199
|
run_with_exit(target)
|
@@ -137,22 +204,27 @@ module StackCar
|
|
137
204
|
|
138
205
|
desc "deploy ENVIRONMENT", "deploy an image from the registry"
|
139
206
|
def deploy(environment)
|
140
|
-
|
207
|
+
setup
|
208
|
+
run_with_exit("DEPLOY_HOOK=$DEPLOY_HOOK_#{environment.upcase} #{dotenv(environment)} ansible-playbook -i ops/hosts -l #{environment}:localhost ops/deploy.yml")
|
141
209
|
end
|
142
210
|
|
143
|
-
method_option :elasticsearch, default: false, type: :boolean, aliases: '-e'
|
144
|
-
method_option :solr, default: false, type: :boolean, aliases: '-s'
|
145
|
-
method_option :postgres, default: false, type: :boolean, aliases: '-p'
|
146
|
-
method_option :mysql, default: false, type: :boolean, aliases: '-m'
|
147
|
-
method_option :redis, default: false, type: :boolean, aliases: '-r'
|
148
211
|
method_option :delayed_job, default: false, type: :boolean, aliases: '-j'
|
149
|
-
method_option :fcrepo, default: false, type: :boolean, aliases: '-f'
|
150
212
|
method_option :deploy, default: false, type: :boolean, aliases: '-d'
|
213
|
+
method_option :elasticsearch, default: false, type: :boolean, aliases: '-e'
|
214
|
+
method_option :fcrepo, default: false, type: :boolean, aliases: '-f'
|
215
|
+
method_option :helm, default: false, type: :boolean, aliases: '-h'
|
216
|
+
method_option :git, default: false, type: :boolean, aliases: '-g'
|
151
217
|
method_option :heroku, default: false, type: :boolean, aliases: '-h'
|
152
|
-
method_option :
|
153
|
-
method_option :
|
154
|
-
method_option :
|
155
|
-
method_option :
|
218
|
+
method_option :hyku, default: false, type: :boolean, aliases: "\--hu"
|
219
|
+
method_option :imagemagick, default: false, type: :boolean, aliases: '-i'
|
220
|
+
method_option :memcached, default: false, type: :boolean, aliases: "\--mc"
|
221
|
+
method_option :mongodb, default: false, type: :boolean, aliases: "\--mg"
|
222
|
+
method_option :mysql, default: false, type: :boolean, aliases: '-m'
|
223
|
+
method_option :postgres, default: false, type: :boolean, aliases: '-p'
|
224
|
+
method_option :rancher, default: false, type: :boolean, aliases: "\--dr"
|
225
|
+
method_option :redis, default: false, type: :boolean, aliases: '-r'
|
226
|
+
method_option :sidekiq, default: false, type: :boolean, aliases: "\--sk"
|
227
|
+
method_option :solr, default: false, type: :boolean, aliases: '-s'
|
156
228
|
method_option :yarn, default: false, type: :boolean, aliases: '-y'
|
157
229
|
desc 'dockerize DIR', 'Will copy the docker tempates in to your project, see options for supported dependencies'
|
158
230
|
long_desc <<-DOCKERIZE
|
@@ -163,9 +235,14 @@ module StackCar
|
|
163
235
|
DOCKERIZE
|
164
236
|
def dockerize(dir=".")
|
165
237
|
Dir.chdir(dir)
|
238
|
+
self.destination_root = dir
|
239
|
+
setup
|
240
|
+
# Commandline overrides config files
|
241
|
+
# options = file_config.merge(options)
|
166
242
|
@project_name = File.basename(File.expand_path(dir))
|
167
243
|
apt_packages << "libpq-dev postgresql-client" if options[:postgres]
|
168
244
|
apt_packages << "mysql-client" if options[:mysql]
|
245
|
+
apt_packages << "imagemagick" if options[:imagemagick]
|
169
246
|
pre_apt << "echo 'Downloading Packages'"
|
170
247
|
post_apt << "echo 'Packages Downloaded'"
|
171
248
|
|
@@ -185,13 +262,19 @@ module StackCar
|
|
185
262
|
post_apt << "cd /opt && unzip fits-1.0.5.zip && chmod +X fits-1.0.5/fits.sh"
|
186
263
|
end
|
187
264
|
|
188
|
-
['.dockerignore', 'Dockerfile', '
|
265
|
+
['.dockerignore', 'Dockerfile', 'docker-compose.yml', '.gitlab-ci.yml', '.env'].each do |template_file|
|
189
266
|
puts template_file
|
190
267
|
template("#{template_file}.erb", template_file)
|
191
268
|
end
|
192
|
-
|
269
|
+
directory('.gitlab', '.gitlab')
|
193
270
|
template(".env.development.erb", ".env.development")
|
194
271
|
template(".env.erb", ".env.production")
|
272
|
+
template(".sops.yaml.erb", ".sops.yaml")
|
273
|
+
template("decrypt-secrets", "bin/decrypt-secrets")
|
274
|
+
template("encrypt-secrets", "bin/encrypt-secrets")
|
275
|
+
template("database.yml.erb", "config/database.yml")
|
276
|
+
template("development.rb.erb", "config/environments/development.rb")
|
277
|
+
template("production.rb.erb", "config/environments/production.rb")
|
195
278
|
|
196
279
|
if File.exists?('README.md')
|
197
280
|
prepend_to_file "README.md" do
|
@@ -202,14 +285,33 @@ module StackCar
|
|
202
285
|
File.read("#{self.class.source_root}/README.md")
|
203
286
|
end
|
204
287
|
end
|
205
|
-
|
288
|
+
|
289
|
+
if File.exist?('Gemfile')
|
290
|
+
append_to_file('Gemfile', "gem 'activerecord-nulldb-adapter'")
|
291
|
+
else
|
292
|
+
append_to_file('../Gemfile', "gem 'activerecord-nulldb-adapter'", { verbose: false })
|
293
|
+
# TODO: remove '../' from message after other status messages are prepended with 'stack_car/'
|
294
|
+
append_to_file("../Gemfile", "gem 'pronto', groups: [:development, :test]")
|
295
|
+
append_to_file("../Gemfile", "gem 'pronto-rubocop', groups: [:development, :test]")
|
296
|
+
say_status(:append, '../Gemfile')
|
297
|
+
end
|
298
|
+
|
206
299
|
if options[:deploy] || options[:rancher]
|
207
300
|
directory('ops')
|
208
301
|
['hosts', 'deploy.yml', 'provision.yml'].each do |template_file|
|
209
302
|
template("#{template_file}.erb", "ops/#{template_file}")
|
210
303
|
end
|
304
|
+
|
211
305
|
say 'Please update ops/hosts with the correct server addresses'
|
212
|
-
|
306
|
+
elsif options[:helm]
|
307
|
+
directory('chart')
|
308
|
+
if options[:fcrepo]
|
309
|
+
directory('chart-fcrepo', 'chart/templates')
|
310
|
+
end
|
311
|
+
if options[:sidekiq]
|
312
|
+
directory('chart-sidekiq', 'chart/templates')
|
313
|
+
end
|
314
|
+
else
|
213
315
|
empty_directory('ops')
|
214
316
|
end
|
215
317
|
|
@@ -224,7 +326,7 @@ module StackCar
|
|
224
326
|
protected
|
225
327
|
def compose_depends(*excludes)
|
226
328
|
@compose_depends = []
|
227
|
-
services = [:postgres, :mysql, :elasticsearch, :solr, :redis, :mongodb, :memcached] - excludes
|
329
|
+
services = [:fcrepo, :postgres, :mysql, :elasticsearch, :sidekiq, :solr, :redis, :mongodb, :memcached] - excludes
|
228
330
|
services.each do |service|
|
229
331
|
if options[service]
|
230
332
|
@compose_depends << " - #{service}"
|
@@ -264,5 +366,115 @@ module StackCar
|
|
264
366
|
end
|
265
367
|
end
|
266
368
|
|
369
|
+
def file_config
|
370
|
+
path = find_config(Dir.pwd)
|
371
|
+
if path
|
372
|
+
JSON.parse(File.read(path))
|
373
|
+
else
|
374
|
+
{}
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def find_config(dir)
|
379
|
+
path = File.join(dir, '.stackcar_rc')
|
380
|
+
if File.exists?(path)
|
381
|
+
return path
|
382
|
+
elsif dir == "/"
|
383
|
+
return nil
|
384
|
+
else
|
385
|
+
return find_config(File.dirname(dir))
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def ensure_development_env
|
390
|
+
if !File.exists?('.env.development')
|
391
|
+
template(".env.development.erb", ".env.development")
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def dotenv(environment='development')
|
396
|
+
"dotenv -f .env.#{environment},.env"
|
397
|
+
end
|
398
|
+
|
399
|
+
def setup
|
400
|
+
if File.exists?('stack_car')
|
401
|
+
@sc_dir = true
|
402
|
+
Dir.chdir('stack_car')
|
403
|
+
self.destination_root += "/stack_car"
|
404
|
+
end
|
405
|
+
DotRc.new
|
406
|
+
end
|
407
|
+
|
408
|
+
def remove_container(service_name, remove_volumes)
|
409
|
+
container = find_container_by_service(service_name)
|
410
|
+
|
411
|
+
container.map do |id, name|
|
412
|
+
prompt_run_confirmation("Remove #{name} container?")
|
413
|
+
|
414
|
+
if `docker container ls --format "{{.Names}}"`.include?(name)
|
415
|
+
say 'Stopping container...'
|
416
|
+
`docker stop #{id}`
|
417
|
+
end
|
418
|
+
|
419
|
+
say 'Removing container...'
|
420
|
+
`docker container rm #{id}`
|
421
|
+
|
422
|
+
# Ensure container was removed
|
423
|
+
if `docker ps -aqf id=#{id}`.empty?
|
424
|
+
say " Container #{name} was removed"
|
425
|
+
else
|
426
|
+
say ">>> There was an issue removing container #{name} (#{id})"
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
remove_volumes_mounted_to_container(@container_volume_names) if remove_volumes
|
431
|
+
end
|
432
|
+
|
433
|
+
def find_container_by_service(service_name)
|
434
|
+
container_id = `docker-compose ps -aq #{service_name}`.strip
|
435
|
+
|
436
|
+
if container_id.empty?
|
437
|
+
say "Unable to locate a container for the service '#{service_name}'"
|
438
|
+
say "Try running `docker-compose ps #{service_name}` to make sure the container exists"
|
439
|
+
exit(1)
|
440
|
+
end
|
441
|
+
|
442
|
+
get_volume_names_for_container(container_id)
|
443
|
+
container_name = `docker ps -af id=#{container_id} --format "{{.Names}}"`.strip
|
444
|
+
|
445
|
+
{ container_id => container_name }
|
446
|
+
end
|
447
|
+
|
448
|
+
def remove_volumes_mounted_to_container(volumes)
|
449
|
+
return if volumes.empty?
|
450
|
+
|
451
|
+
prompt_run_confirmation("\n#{volumes.join("\n")}\nRemove these volume(s)?")
|
452
|
+
volumes.each do |v|
|
453
|
+
say 'Removing volume...'
|
454
|
+
`docker volume rm #{v}`
|
455
|
+
|
456
|
+
if `docker volume ls -q`.include?(v)
|
457
|
+
say ">>> There was an issue removing volume #{v}"
|
458
|
+
else
|
459
|
+
say " Volume #{v} was removed"
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def get_volume_names_for_container(container_id)
|
465
|
+
@container_volume_names ||= []
|
466
|
+
return @container_volume_names unless @container_volume_names.empty?
|
467
|
+
|
468
|
+
JSON.parse(`docker inspect --format="{{json .Mounts}}" #{container_id}`).map do |mount_info|
|
469
|
+
@container_volume_names << mount_info['Name'] if mount_info['Type'] == 'volume'
|
470
|
+
end
|
471
|
+
|
472
|
+
@container_volume_names
|
473
|
+
end
|
474
|
+
|
475
|
+
def prompt_run_confirmation(question)
|
476
|
+
response = ask(question, limited_to: %w[y n])
|
477
|
+
exit(1) unless response == 'y'
|
478
|
+
end
|
267
479
|
end
|
268
480
|
end
|