stack_car 0.8.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|