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.
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