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.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.gitlab-ci.yml +17 -12
  3. data/README.md +122 -1
  4. data/lib/stack_car/cli.rb +253 -41
  5. data/lib/stack_car/dot_rc.rb +25 -0
  6. data/lib/stack_car/version.rb +1 -1
  7. data/lib/stack_car.rb +1 -0
  8. data/stack_car.gemspec +2 -0
  9. data/templates/.dockerignore.erb +2 -2
  10. data/templates/.env.development.erb +2 -0
  11. data/templates/.env.erb +29 -16
  12. data/templates/.gitlab/issue_templates/Bug.md +46 -0
  13. data/templates/.gitlab/issue_templates/Feature.md +41 -0
  14. data/templates/.gitlab/issue_templates/Question.md +18 -0
  15. data/templates/.gitlab/merge_request_templates/Bug.md +36 -0
  16. data/templates/.gitlab/merge_request_templates/Feature.md +36 -0
  17. data/templates/.gitlab-ci.yml.erb +98 -65
  18. data/templates/.sops.yaml.erb +3 -0
  19. data/templates/Dockerfile.erb +26 -7
  20. data/templates/README.md +81 -7
  21. data/templates/chart/.gitignore +3 -0
  22. data/templates/chart/.helmignore +23 -0
  23. data/templates/chart/Chart.yaml.tt +30 -0
  24. data/templates/chart/README.md +223 -0
  25. data/templates/chart/bin/check_sidekiq.rb +0 -0
  26. data/templates/chart/bin/decrypt +17 -0
  27. data/templates/chart/bin/deploy +14 -0
  28. data/templates/chart/bin/encrypt +15 -0
  29. data/templates/chart/bin/remove +15 -0
  30. data/templates/chart/sample-values.yaml.tt +153 -0
  31. data/templates/chart/templates/_helpers.tpl.tt +85 -0
  32. data/templates/chart/templates/rails-env-cm.yaml.tt +47 -0
  33. data/templates/chart/templates/rails-env-secret.yaml +10 -0
  34. data/templates/chart/templates/rails-pvc-shared.yml +20 -0
  35. data/templates/chart/templates/setup-job.yaml +73 -0
  36. data/templates/chart/templates/web-deploy.yaml +67 -0
  37. data/templates/chart/templates/web-ing-wildcard.yaml +20 -0
  38. data/templates/chart/templates/web-ing.yaml +20 -0
  39. data/templates/chart/templates/web-svc.yaml +20 -0
  40. data/templates/chart-fcrepo/fcrepo-deploy.yaml +63 -0
  41. data/templates/chart-fcrepo/fcrepo-env-cm.yaml +8 -0
  42. data/templates/chart-fcrepo/fcrepo-env-secret.yaml.tt +10 -0
  43. data/templates/chart-fcrepo/fcrepo-pvc.yaml +20 -0
  44. data/templates/chart-fcrepo/fcrepo-svc.yaml +19 -0
  45. data/templates/chart-sidekiq/sidekiq-deploy.yaml +80 -0
  46. data/templates/database.yml.erb +10 -11
  47. data/templates/decrypt-secrets +22 -0
  48. data/templates/development.rb.erb +90 -0
  49. data/templates/docker-compose.yml.erb +52 -18
  50. data/templates/encrypt-secrets +19 -0
  51. data/templates/env.conf.erb +11 -11
  52. data/templates/nginx.sh.erb +17 -0
  53. data/templates/production.rb.erb +117 -0
  54. metadata +71 -12
  55. data/templates/Dockerfile.base.erb +0 -48
  56. data/templates/Dockerfile.builder.erb +0 -13
  57. data/templates/docker-compose.ci.yml.erb +0 -87
  58. data/templates/docker-compose.production.yml.erb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 185a0b49e3f2bdac8994507a50f3454d1455df64
4
- data.tar.gz: 1a924d44567950615378184317b421e9c9ccee51
2
+ SHA256:
3
+ metadata.gz: 80132883ba506f9cf4414a46f560a2040e8843596b7a94e3d6f9baab6ae3a4d4
4
+ data.tar.gz: bb84c75a79f699e7bb04aa877eaed36f3acb9e8c8d9bf294d9f4fa7bbe579ddc
5
5
  SHA512:
6
- metadata.gz: c65cd894261123a12ebf2ac3f47a39b57a44aea50f87e1d481e255b755c9edc490ffa32dd4ae9e61e777be0cc3c44e89e2b43ea885ffe236da8f3540577862f1
7
- data.tar.gz: 24e5e23aceb5cbd161017047ef4ee1d54b01288877d21ad3e8598659bd90b8f5c9f617c005a31e30c346bff01295ca2dbff3037737bfe7e1edfd3d53fb4e8ce6
6
+ metadata.gz: 1e03c8620fcd08038774e75c2cfeefa75a2bae7d42708c0240c774e3584391695a87349c50bba6b0c7b444e46e66d73b2e96b1439bbf09f0104670b83e3fb831
7
+ data.tar.gz: 3196614e6fd6ce06b7408aa292e4ae0b19ef57aa9df8643899fcf378ad840a2d4d469695010ca04948676b9a46b7b655947cbaa0bf686c59c27b3b385b73341a
data/.gitlab-ci.yml CHANGED
@@ -1,17 +1,22 @@
1
- image: ruby:2.3
1
+ stages:
2
+ - rspec
2
3
 
3
4
  before_script:
4
- bundle
5
+ - bundle
5
6
 
6
- pages:
7
- script:
8
- - bundle exec yardoc --plugin yard-thor -o public - templates/.* templates/*
9
- artifacts:
10
- paths:
11
- - public
12
- only:
13
- - master
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
- test:
16
+ rspec:
17
+ stage: rspec
18
+ image: ruby:2.3
16
19
  script:
17
- bundle exec rake
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, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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
- run("docker-compose stop #{options[:service]}")
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
- map down: :stop
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
- run_with_exit("docker-compose build #{options[:service]}")
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
- run_with_exit("docker-compose push #{options[:service]} #{args.join(' ')}")
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
- run_with_exit("docker-compose pull #{options[:service]} #{args.join(' ')}")
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
- run_with_exit("docker-compose ps #{options[:service]} #{args.join(' ')}")
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
- run_with_exit("docker-compose exec #{options[:service]} bundle")
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
- run_with_exit("docker-compose run #{options[:service]} #{args.join(' ')}")
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
- run_with_exit("docker-compose exec #{options[:service]} #{args.join(' ')}")
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
- run_with_exit("docker-compose exec -e COLUMNS=\"\`tput cols\`\" -e LINES=\"\`tput lines\`\" #{options[:service]} bash #{args.join(' ')}")
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
- run_with_exit("docker-compose exec #{options[:service]} bundle exec #{args.join(' ')}")
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
- run_with_exit("docker-compose exec #{options[:service]} bundle exec rails console #{args.join(' ')}")
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
- run_with_exit("DEPLOY_HOOK=$DEPLOY_HOOK_#{environment.upcase} dotenv ansible-playbook -i ops/hosts -l #{environment}:localhost ops/deploy.yml")
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 :rancher, default: false, type: :boolean, aliases: '-dr'
153
- method_option :sidekiq, default: false, type: :boolean, aliases: '-sq' # TODO
154
- method_option :mongodb, default: false, type: :boolean, aliases: '-mg'
155
- method_option :memcached, default: false, type: :boolean, aliases: '-mc'
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', 'Dockerfile.base', 'docker-compose.yml', 'docker-compose.ci.yml', 'docker-compose.production.yml', '.gitlab-ci.yml', '.env'].each do |template_file|
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
- template("database.yml.erb", "config/database.yml")
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
- append_to_file("Gemfile", "gem 'activerecord-nulldb-adapter'")
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
- else
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