convox_installer 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.vscode/settings.json +3 -2
- data/Gemfile +1 -0
- data/README.md +109 -39
- data/examples/full_installation.rb +22 -15
- data/lib/convox/client.rb +194 -180
- data/lib/convox_installer/config.rb +8 -0
- data/lib/convox_installer/requirements.rb +15 -4
- data/lib/convox_installer/version.rb +1 -1
- data/lib/convox_installer.rb +10 -4
- data/spec/lib/convox/client_spec.rb +1 -1
- data/spec/lib/convox_installer/requirements_spec.rb +5 -6
- data/terraform/elasticache.tf.erb +46 -0
- data/terraform/rds.tf.erb +45 -0
- data/terraform/s3_bucket.tf.erb +73 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea8a81c025b1a5046d3145beeee5cba92f8448cc8751182d911f3548b289c31a
|
4
|
+
data.tar.gz: 8eb61f167e06eb67bcd86eada0196d138995b3e61b237ce04f11891f6eb1426c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db7f9e671230b4054a888397461056af9f8ee2d631e0d05fb4eda32a138ae947e7f7fdee8706ea3b9b695254d9fa8b5c2971222a68d8ba4ad8a1326917357f65
|
7
|
+
data.tar.gz: 8dd7d82786491eca18253d6dc405675fbe6c188c0276878ed352e3f9c17770aca9a4906b8819d061dd6940a203832e0808e9f1aa8427bddc9505d89974f39c69
|
data/.rubocop.yml
CHANGED
data/.vscode/settings.json
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,23 @@
|
|
1
1
|
# Convox Installer
|
2
2
|
|
3
|
-
A Ruby gem that makes it easier to build a Convox installation script.
|
3
|
+
A Ruby gem that makes it easier to build a Convox installation script. The main purpose of this gem is to make it easier to set up on-premise installations of your app for enterprise users.
|
4
4
|
|
5
|
-
|
5
|
+
This gem provides a DSL so that you can write a script that walks your users through setting up Convox and getting your app and running, setting up S3 buckets, etc.
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
|
9
|
+
- MacOS
|
10
|
+
- Convox v3 CLI
|
11
|
+
- Runtime integration installed in your AWS account. See: https://docs.convox.com/getting-started/introduction/
|
12
|
+
|
13
|
+
_Please let us know if you need to run this script on Linux. Linux support should not be too difficult to implement, but unfortunately we probably won't be able to support Windows._
|
14
|
+
|
15
|
+
### Requires Convox >= 3
|
16
|
+
|
17
|
+
This version of `convox_installer` is only designed to work with Convox 3 and later. You can run `convox version` to check your version. Please install the Convox v3 CLI by following the instructions here: https://docs.convox.com/getting-started/introduction/
|
18
|
+
|
19
|
+
_If you want to set up a Convox v2 rack (deprecated), the last version of `convox_installer` that supports the v2 CLI is `1.0.9`. (Take a look at [the `convox2` branch](https://github.com/DocSpring/convox_installer/tree/convox2).)_
|
6
20
|
|
7
|
-
This version of `convox_installer` is only designed to work with Convox v2. Please install the v2 CLI by following the instructions at their old documentation site: https://docsv2.convox.com/introduction/installation
|
8
21
|
## USE AT YOUR OWN RISK! THIS CODE IS PROVIDED WITHOUT ANY WARRANTIES OR GUARANTEES
|
9
22
|
|
10
23
|
We have successfully set up a number of test and production deployments using this gem. Everything seems to work very well. The library also facilitates idempotency and crash-resistance, so you can easily re-run your installation script if something goes wrong. However, if anything goes wrong, then you can end up with a large AWS bill if you're not careful. If anything crashes then make sure you double-check everything in your AWS account and shut down any leftover resources. **USE THIS SOFTWARE AT YOUR OWN RISK.**
|
@@ -25,7 +38,7 @@ We have successfully set up a number of test and production deployments using th
|
|
25
38
|
|
26
39
|
`convox_installer` is a Ruby gem that makes it much easier to build an installation script for `convox/rack` (the open source PaaS). The Convox CLI is awesome, but it's missing a nice way to script a full deployment. I originally wrote a bash script that made API calls and used [`jq`](https://stedolan.github.io/jq/) and `sed`, but this was very error-prone and it did not have good cross-platform support.
|
27
40
|
|
28
|
-
I've
|
41
|
+
I've written this installation script in Ruby, which provides very good cross-platform support, and also allows me to write tests.
|
29
42
|
|
30
43
|
## Usage
|
31
44
|
|
@@ -37,7 +50,7 @@ require 'bundler/inline'
|
|
37
50
|
|
38
51
|
gemfile do
|
39
52
|
source 'https://rubygems.org'
|
40
|
-
gem 'convox_installer'
|
53
|
+
gem 'convox_installer', '3.0.0'
|
41
54
|
end
|
42
55
|
|
43
56
|
require "convox_installer"
|
@@ -141,10 +154,6 @@ Makes sure that the `convox` and `aws` CLI tools are installed on this system. I
|
|
141
154
|
Loads config from ENV vars, or from saved config at `./.installer_config.json`.
|
142
155
|
If any config settings are missing, it prompts the user for input. Finally, it shows a summary of the config, and asks the user if they want to proceed with the installation. If the user enters `y` (or `yes`), the `prompt_for_config` method completes. If they enter `n` (or `no`), we loop over every setting and let them press "enter" to keep the current value, or provide a new value to correct any mistakes.
|
143
156
|
|
144
|
-
#### `backup_convox_host_and_rack`
|
145
|
-
|
146
|
-
If there are any existing files at `~/.convox/host` or `~/.convox/rack`, this method moves these to `~/.convox/host.bak` and `~/.convox/rack.bak`.
|
147
|
-
|
148
157
|
#### `install_convox`
|
149
158
|
|
150
159
|
- **Required Config:** `aws_region`, `aws_access_key_id`, `aws_secret_access_key`,
|
@@ -152,13 +161,13 @@ If there are any existing files at `~/.convox/host` or `~/.convox/rack`, this me
|
|
152
161
|
|
153
162
|
Runs `convox rack install ...`. Has some validations to ensure that all required settings are present.
|
154
163
|
|
155
|
-
#### `
|
164
|
+
#### `validate_convox_rack_and_write_current!`
|
156
165
|
|
157
|
-
|
166
|
+
Ensures that the local machine contains a directory for the rack's terraform config, and sets the current rack for Convox CLI commands.
|
158
167
|
|
159
|
-
#### `
|
168
|
+
#### `validate_convox_rack_api!`
|
160
169
|
|
161
|
-
|
170
|
+
Makes an API request (`convox api get /system`) to get the rack details, and makes sure that everything is correct.
|
162
171
|
|
163
172
|
#### `convox_rack_data`
|
164
173
|
|
@@ -174,10 +183,14 @@ Checks if the app already exists. If not, calls `convox apps create ... --wait`
|
|
174
183
|
|
175
184
|
Writes the app name into `./.convox/app` (in the current directory.) The `convox` CLI reads this file, so you don't need to specify the `--app` flag for future commands.
|
176
185
|
|
177
|
-
#### `
|
186
|
+
#### `add_s3_bucket`
|
187
|
+
|
188
|
+
Adds an S3 bucket to your Terraform config.
|
178
189
|
|
179
190
|
- **Required Config:** `s3_bucket_name`
|
180
191
|
|
192
|
+
NOTE: This method just writes a new Terraform configuration file. You must run `apply_terraform_update!` to apply the changes and create the S3 bucket.
|
193
|
+
|
181
194
|
Creates an S3 bucket from the `:s3_bucket_name` config setting. This is not a default setting, so you can add something like this to your custom `@prompts`:
|
182
195
|
|
183
196
|
```ruby
|
@@ -190,41 +203,97 @@ Creates an S3 bucket from the `:s3_bucket_name` config setting. This is not a de
|
|
190
203
|
|
191
204
|
The `:value` `Proc` will generate a bucket name with a random suffix. (Avoids conflicts when you are setting up multiple deployments for your app.)
|
192
205
|
|
193
|
-
|
194
|
-
|
195
|
-
#### `set_s3_bucket_cors_policy`
|
206
|
+
You can also set a CORS policy for your S3 bucket. (`:s3_bucket_name`)
|
207
|
+
We set the `cors_rule` option for the `aws_s3_bucket` resource in the Terraform configuration. Example:
|
196
208
|
|
197
|
-
|
209
|
+
```
|
210
|
+
cors_rule {
|
211
|
+
allowed_headers = ["*"]
|
212
|
+
allowed_methods = ["PUT", "POST"]
|
213
|
+
allowed_origins = ["https://s3-website-test.hashicorp.com"]
|
214
|
+
expose_headers = ["ETag"]
|
215
|
+
max_age_seconds = 3000
|
216
|
+
}
|
217
|
+
```
|
198
218
|
|
199
|
-
|
219
|
+
See: https://registry.terraform.io/providers/hashicorp/aws/3.33.0/docs/resources/s3_bucket#using-cors
|
200
220
|
|
201
|
-
_Note: If the `:
|
221
|
+
_Note: If the `:s3_bucket_cors_rule` setting is not provided, then it is skipped._
|
202
222
|
|
203
|
-
|
223
|
+
Here's how we set up a CORS policy in our own `install.rb` script:
|
204
224
|
|
205
225
|
```ruby
|
206
|
-
|
207
|
-
{
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
]
|
216
|
-
}
|
217
|
-
JSON
|
226
|
+
xxxxc = <<-TERRAFORM
|
227
|
+
cors_rule {
|
228
|
+
allowed_headers = ["Authorization", "cache-control", "x-requested-with"]
|
229
|
+
allowed_methods = ["PUT", "POST", "GET"]
|
230
|
+
allowed_origins = ["*"]
|
231
|
+
expose_headers = []
|
232
|
+
max_age_seconds = 3000
|
233
|
+
}
|
234
|
+
TERRAFORM
|
218
235
|
|
219
236
|
@prompts = [
|
220
237
|
{
|
221
|
-
key: :
|
222
|
-
value:
|
238
|
+
key: :s3_bucket_cors_rule,
|
239
|
+
value: S3_BUCKET_CORS_RULE,
|
223
240
|
hidden: true,
|
224
241
|
}
|
225
242
|
]
|
226
243
|
```
|
227
244
|
|
245
|
+
#### `add_rds_database`
|
246
|
+
|
247
|
+
Adds an RDS database to your Terraform config.
|
248
|
+
|
249
|
+
- **Required Config:**
|
250
|
+
- `database_username`
|
251
|
+
- `database_password`
|
252
|
+
- **Optional Config:**
|
253
|
+
- `database_allocated_storage` _(default: 30)_
|
254
|
+
- `database_engine` _(default: 'postgres')_
|
255
|
+
- `database_engine_version` _(default: '14.2')_
|
256
|
+
- `database_instance_class` _(default: 'db.t3.medium')_
|
257
|
+
- `database_multi_az` _(default: true)_
|
258
|
+
|
259
|
+
#### `add_add_elasticache_cluster`
|
260
|
+
|
261
|
+
Adds an Elasticache cluster to your Terraform config.
|
262
|
+
|
263
|
+
- **Optional Config:**
|
264
|
+
- `engine` _(default: 'redis')_
|
265
|
+
- `engine_version` _(default: '6.2.5')_
|
266
|
+
- `node_type` _(default: 'cache.m3.medium')_
|
267
|
+
- `database_instance_class` _(default: 'db.t3.medium')_
|
268
|
+
- `num_cache_nodes` _(default: 1)_
|
269
|
+
- `port` _(default: 6379)_
|
270
|
+
|
271
|
+
_IMPORTANT: Make sure you specify a full version string (e.g. `6.2.5`), and not a partial version (e.g. `6.2`.) A partial version will cause Terraform to delete and recreate the cluster on every run._
|
272
|
+
|
273
|
+
#### `apply_terraform_update!`
|
274
|
+
|
275
|
+
Runs `terraform apply -auto-approve` to apply any changes to your Terraform configuration (add new resources, etc.)
|
276
|
+
|
277
|
+
#### `rds_details`
|
278
|
+
|
279
|
+
Returns information about the created RDS database resource.
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
{
|
283
|
+
postgres_url: "Full URL for the RDS database (including auth)",
|
284
|
+
}
|
285
|
+
```
|
286
|
+
|
287
|
+
#### `elasticache_details`
|
288
|
+
|
289
|
+
Returns information about the created RDS database resource.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
{
|
293
|
+
redis_url: "Full URL for the Redis cluster",
|
294
|
+
}
|
295
|
+
```
|
296
|
+
|
228
297
|
#### `s3_bucket_details`
|
229
298
|
|
230
299
|
- **Required Config:** `s3_bucket_name`
|
@@ -249,13 +318,14 @@ Checks the list of registries to see if `docker_registry_url` has already been a
|
|
249
318
|
|
250
319
|
#### `default_service_domain_name`
|
251
320
|
|
252
|
-
- **Required Config:** `convox_app_name
|
321
|
+
- **Required Config:** `convox_app_name`
|
322
|
+
- **Optional Config:** `default_service`
|
253
323
|
|
254
|
-
|
324
|
+
Finds the default `*.convox.cloud` URL for the web service. (You can visit this URL in the browser to access your app.)
|
255
325
|
|
256
|
-
Example: `
|
326
|
+
Example: `web.docspring.dc6bae48c2e36366.convox.cloud`
|
257
327
|
|
258
|
-
|
328
|
+
You can override the default service name in your config (e.g. `web`):
|
259
329
|
|
260
330
|
```ruby
|
261
331
|
@prompts = [
|
@@ -20,18 +20,15 @@ include ConvoxInstaller
|
|
20
20
|
MINIMAL_HEALTH_CHECK_PATH = '/health/site'
|
21
21
|
COMPLETE_HEALTH_CHECK_PATH = '/health'
|
22
22
|
|
23
|
-
|
24
|
-
{
|
25
|
-
"
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
"MaxAgeSeconds": 3000
|
31
|
-
}
|
32
|
-
]
|
23
|
+
S3_BUCKET_CORS_RULE = <<-TERRAFORM
|
24
|
+
cors_rule {
|
25
|
+
allowed_headers = ["Authorization", "cache-control", "x-requested-with"]
|
26
|
+
allowed_methods = ["PUT", "POST", "GET"]
|
27
|
+
allowed_origins = ["*"]
|
28
|
+
expose_headers = []
|
29
|
+
max_age_seconds = 3000
|
33
30
|
}
|
34
|
-
|
31
|
+
TERRAFORM
|
35
32
|
|
36
33
|
@prompts = ConvoxInstaller::Config::DEFAULT_PROMPTS + [
|
37
34
|
{
|
@@ -81,8 +78,18 @@ JSON
|
|
81
78
|
value: -> { "app-uploads-#{SecureRandom.hex(4)}" }
|
82
79
|
},
|
83
80
|
{
|
84
|
-
key: :
|
85
|
-
value:
|
81
|
+
key: :s3_bucket_cors_rule,
|
82
|
+
value: S3_BUCKET_CORS_RULE,
|
83
|
+
hidden: true
|
84
|
+
},
|
85
|
+
{
|
86
|
+
key: :database_username,
|
87
|
+
value: 'example_app',
|
88
|
+
hidden: true
|
89
|
+
},
|
90
|
+
{
|
91
|
+
key: :database_password,
|
92
|
+
value: -> { SecureRandom.hex(16) },
|
86
93
|
hidden: true
|
87
94
|
}
|
88
95
|
]
|
@@ -94,7 +101,7 @@ backup_convox_host_and_rack
|
|
94
101
|
install_convox
|
95
102
|
|
96
103
|
validate_convox_auth_and_write_host!
|
97
|
-
|
104
|
+
validate_convox_rack_api!
|
98
105
|
|
99
106
|
create_convox_app!
|
100
107
|
set_default_app_for_directory!
|
@@ -129,7 +136,7 @@ env_command_params = env.map { |k, v| "#{k}=\"#{v}\"" }.join(' ')
|
|
129
136
|
run_convox_command! "env set #{env_command_params}"
|
130
137
|
|
131
138
|
puts '=> Initial deploy...'
|
132
|
-
puts '-----> Documentation: https://docs.convox.com/deployment/
|
139
|
+
puts '-----> Documentation: https://docs.convox.com/deployment/deploying-changes/'
|
133
140
|
run_convox_command! 'deploy --wait'
|
134
141
|
|
135
142
|
puts '=> Setting up the database...'
|
data/lib/convox/client.rb
CHANGED
@@ -4,12 +4,20 @@ require 'logger'
|
|
4
4
|
require 'json'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'rubygems'
|
7
|
+
require 'os'
|
8
|
+
require 'erb'
|
7
9
|
|
8
10
|
module Convox
|
9
11
|
class Client
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
CONVOX_CONFIG_DIR = if OS.mac?
|
13
|
+
# Convox v3 moved this to ~/Library/Preferences/convox/ on Mac
|
14
|
+
File.expand_path('~/Library/Preferences/convox').freeze
|
15
|
+
else
|
16
|
+
File.expand_path('~/.convox').freeze
|
17
|
+
end
|
18
|
+
|
19
|
+
AUTH_FILE = File.join(CONVOX_CONFIG_DIR, 'auth')
|
20
|
+
CURRENT_FILE = File.join(CONVOX_CONFIG_DIR, 'current')
|
13
21
|
|
14
22
|
attr_accessor :logger, :config
|
15
23
|
|
@@ -57,17 +65,21 @@ module Convox
|
|
57
65
|
@config = options[:config] || {}
|
58
66
|
end
|
59
67
|
|
68
|
+
# Convox v3 creates a folder for each rack for the Terraform config
|
69
|
+
def rack_dir
|
70
|
+
stack_name = config.fetch(:stack_name)
|
71
|
+
File.join(CONVOX_CONFIG_DIR, 'racks', stack_name)
|
72
|
+
end
|
73
|
+
|
60
74
|
def backup_convox_host_and_rack
|
61
|
-
FileUtils.mkdir_p
|
75
|
+
FileUtils.mkdir_p CONVOX_CONFIG_DIR
|
62
76
|
|
63
|
-
|
64
|
-
|
65
|
-
next unless File.exist?(path)
|
77
|
+
path = File.join(CONVOX_CONFIG_DIR, 'current')
|
78
|
+
return unless File.exist?(path)
|
66
79
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
80
|
+
bak_file = "#{path}.bak"
|
81
|
+
logger.info "Moving existing #{path} to #{bak_file}..."
|
82
|
+
FileUtils.mv(path, bak_file)
|
71
83
|
end
|
72
84
|
|
73
85
|
def install_convox
|
@@ -76,8 +88,12 @@ module Convox
|
|
76
88
|
stack_name = config.fetch(:stack_name)
|
77
89
|
|
78
90
|
if rack_already_installed?
|
79
|
-
logger.info "There is already a Convox
|
80
|
-
|
91
|
+
logger.info "There is already a Convox rack named #{stack_name}. Using this rack."
|
92
|
+
logger.debug 'If you need to start over, you can run: ' \
|
93
|
+
"convox rack uninstall #{stack_name} " \
|
94
|
+
'(Make sure you export AWS_ACCESS_KEY_ID and ' \
|
95
|
+
"AWS_SECRET_ACCESS_KEY first.)\n" \
|
96
|
+
"If this fails, you can try deleting the rack directory: rm -rf #{rack_dir}"
|
81
97
|
return true
|
82
98
|
end
|
83
99
|
|
@@ -97,11 +113,19 @@ module Convox
|
|
97
113
|
'AWS_SECRET_ACCESS_KEY' => config.fetch(:aws_secret_access_key)
|
98
114
|
}
|
99
115
|
command = %(rack install aws \
|
100
|
-
|
101
|
-
"
|
102
|
-
"
|
116
|
+
"#{config.fetch(:stack_name)}" \
|
117
|
+
"node_type=#{config.fetch(:instance_type)}" \
|
118
|
+
"region=#{config.fetch(:aws_region)}")
|
119
|
+
# us-east constantly has problems with the us-east-1c AZ:
|
120
|
+
# "Cannot create cluster 'ds-enterprise-cx3' because us-east-1c, the targeted
|
121
|
+
# availability zone, does not currently have sufficient capacity to support the cluster.
|
122
|
+
# Retry and choose from these availability zones:
|
123
|
+
# us-east-1a, us-east-1b, us-east-1d, us-east-1e, us-east-1f
|
124
|
+
if config.fetch(:aws_region) == 'us-east-1'
|
125
|
+
command += ' "availability_zones=us-east-1a,us-east-1b,us-east-1d,us-east-1e,us-east-1f"'
|
126
|
+
end
|
103
127
|
|
104
|
-
run_convox_command!(command, env)
|
128
|
+
run_convox_command!(command, env, rack_arg: false)
|
105
129
|
end
|
106
130
|
|
107
131
|
def rack_already_installed?
|
@@ -109,66 +133,51 @@ module Convox
|
|
109
133
|
|
110
134
|
return unless File.exist?(AUTH_FILE)
|
111
135
|
|
112
|
-
region = config.fetch(:aws_region)
|
136
|
+
# region = config.fetch(:aws_region)
|
113
137
|
stack_name = config.fetch(:stack_name)
|
138
|
+
return true if File.exist?(rack_dir)
|
114
139
|
|
115
|
-
auth.each do |
|
116
|
-
if
|
117
|
-
return true
|
118
|
-
end
|
140
|
+
auth.each do |rack_name, _password|
|
141
|
+
return true if rack_name == stack_name
|
119
142
|
end
|
120
143
|
false
|
121
144
|
end
|
122
145
|
|
123
|
-
|
146
|
+
# Auth for a detached rack is not saved in the auth file anymore.
|
147
|
+
# It can be found in the terraform state:
|
148
|
+
# ~/Library/Preferences/convox/racks/ds-enterprise-cx3/terraform.tfstate
|
149
|
+
# Under outputs/api/value. The API URL contains the convox username and API token as basic auth.
|
150
|
+
def validate_convox_rack_and_write_current!
|
124
151
|
require_config(%i[aws_region stack_name])
|
125
152
|
|
126
|
-
unless
|
127
|
-
raise "Could not find
|
153
|
+
unless rack_already_installed?
|
154
|
+
raise "Could not find rack terraform directory at: #{rack_dir}"
|
128
155
|
end
|
129
156
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
matching_host = nil
|
135
|
-
auth.each do |host, _password|
|
136
|
-
if host.match?(/^#{stack}-\d+\.#{region}\.elb\.amazonaws\.com$/)
|
137
|
-
matching_host = host
|
138
|
-
match_count += 1
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
if match_count == 1
|
143
|
-
write_host(matching_host)
|
144
|
-
return matching_host
|
145
|
-
end
|
146
|
-
|
147
|
-
error_message = if match_count > 1
|
148
|
-
'Found multiple matching hosts for '
|
149
|
-
else
|
150
|
-
'Could not find matching authentication for '
|
151
|
-
end
|
152
|
-
error_message += "region: #{region}, stack: #{stack}"
|
153
|
-
raise error_message
|
157
|
+
# Tells the Convox CLI to use our terraform stack
|
158
|
+
stack_name = config.fetch(:stack_name)
|
159
|
+
write_current(stack_name)
|
160
|
+
stack_name
|
154
161
|
end
|
155
162
|
|
156
|
-
def
|
157
|
-
logger.debug "Setting convox
|
158
|
-
|
163
|
+
def write_current(rack_name)
|
164
|
+
logger.debug "Setting convox rack to #{rack_name} (in #{CURRENT_FILE})..."
|
165
|
+
current_hash = { name: rack_name, type: 'terraform' }
|
166
|
+
File.open(CURRENT_FILE, 'w') { |f| f.puts current_hash.to_json }
|
159
167
|
end
|
160
168
|
|
161
|
-
def
|
169
|
+
def validate_convox_rack_api!
|
162
170
|
require_config(%i[
|
163
171
|
aws_region
|
164
172
|
stack_name
|
165
173
|
instance_type
|
166
174
|
])
|
167
175
|
logger.debug 'Validating that convox rack has the correct attributes...'
|
176
|
+
# Convox 3 racks no longer return info about region or type. (These are blank strings.)
|
168
177
|
{
|
169
178
|
provider: 'aws',
|
170
|
-
region: config.fetch(:aws_region),
|
171
|
-
type: config.fetch(:instance_type),
|
179
|
+
# region: config.fetch(:aws_region),
|
180
|
+
# type: config.fetch(:instance_type),
|
172
181
|
name: config.fetch(:stack_name)
|
173
182
|
}.each do |k, v|
|
174
183
|
convox_value = convox_rack_data[k.to_s]
|
@@ -184,8 +193,22 @@ module Convox
|
|
184
193
|
def convox_rack_data
|
185
194
|
@convox_rack_data ||= begin
|
186
195
|
logger.debug 'Fetching convox rack attributes...'
|
187
|
-
|
188
|
-
|
196
|
+
command = "convox api get /system --rack #{config.fetch(:stack_name)}"
|
197
|
+
logger.debug "+ #{command}"
|
198
|
+
# It can take a while for the API to be ready.
|
199
|
+
start_time = Time.now
|
200
|
+
convox_output = nil
|
201
|
+
loop do
|
202
|
+
convox_output = `#{command}`
|
203
|
+
break if $CHILD_STATUS.success?
|
204
|
+
|
205
|
+
if Time.now - start_time > 360
|
206
|
+
raise 'Could not connect to Convox rack API!'
|
207
|
+
end
|
208
|
+
|
209
|
+
logger.debug 'Waiting for Convox rack API to be ready... (can take a few minutes)'
|
210
|
+
sleep 5
|
211
|
+
end
|
189
212
|
|
190
213
|
JSON.parse(convox_output)
|
191
214
|
end
|
@@ -199,9 +222,10 @@ module Convox
|
|
199
222
|
|
200
223
|
logger.info "Creating app: #{app_name}..."
|
201
224
|
logger.info '=> Documentation: ' \
|
202
|
-
'https://docs.convox.com/
|
225
|
+
'https://docs.convox.com/reference/cli/apps/'
|
203
226
|
|
204
|
-
|
227
|
+
# NOTE: --wait flags were removed in Convox 3. It now waits by default.
|
228
|
+
run_convox_command! "apps create #{app_name}"
|
205
229
|
|
206
230
|
retries = 0
|
207
231
|
loop do
|
@@ -232,7 +256,7 @@ module Convox
|
|
232
256
|
app_name = config.fetch(:convox_app_name)
|
233
257
|
|
234
258
|
logger.debug "Looking for existing #{app_name} app..."
|
235
|
-
convox_output = `convox api get /apps`
|
259
|
+
convox_output = `convox api get /apps --rack #{config.fetch(:stack_name)}`
|
236
260
|
raise 'convox command failed!' unless $CHILD_STATUS.success?
|
237
261
|
|
238
262
|
apps = JSON.parse(convox_output)
|
@@ -247,128 +271,117 @@ module Convox
|
|
247
271
|
end
|
248
272
|
|
249
273
|
# Create the s3 bucket, and also apply a CORS configuration
|
250
|
-
|
274
|
+
# Convox v3 update - They removed support for S3 resources, so we have to do
|
275
|
+
# in terraform now (which is actually pretty nice!)
|
276
|
+
def add_s3_bucket
|
251
277
|
require_config(%i[s3_bucket_name])
|
252
|
-
bucket_name = config.fetch(:s3_bucket_name)
|
253
|
-
if s3_bucket_exists?
|
254
|
-
logger.info "#{bucket_name} S3 bucket already exists!"
|
255
|
-
else
|
256
|
-
logger.info "Creating S3 bucket resource (#{bucket_name})..."
|
257
|
-
run_convox_command! 'rack resources create s3 ' \
|
258
|
-
"--name \"#{bucket_name}\" " \
|
259
|
-
'--wait'
|
260
|
-
|
261
|
-
retries = 0
|
262
|
-
loop do
|
263
|
-
break if s3_bucket_exists?
|
264
278
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
269
|
-
logger.debug 'Waiting for S3 bucket to be ready...'
|
270
|
-
sleep 3
|
271
|
-
retries += 1
|
272
|
-
end
|
273
|
-
|
274
|
-
logger.debug '=> S3 bucket created!'
|
279
|
+
unless config.key? :s3_bucket_cors_rule
|
280
|
+
logger.debug 'No CORS rule provided in config: s3_bucket_cors_rule (optional)'
|
281
|
+
return
|
275
282
|
end
|
276
283
|
|
277
|
-
|
284
|
+
write_terraform_template('s3_bucket')
|
278
285
|
end
|
279
286
|
|
280
|
-
def
|
281
|
-
require_config(%i[
|
282
|
-
|
283
|
-
logger.debug "Looking up S3 bucket resource: #{bucket_name}"
|
284
|
-
`convox api get /resources/#{bucket_name} 2>/dev/null`
|
285
|
-
$CHILD_STATUS.success?
|
287
|
+
def add_rds_database
|
288
|
+
require_config(%i[database_username database_password])
|
289
|
+
write_terraform_template('rds')
|
286
290
|
end
|
287
291
|
|
288
|
-
def
|
289
|
-
|
290
|
-
|
291
|
-
bucket_name = config.fetch(:s3_bucket_name)
|
292
|
-
logger.debug "Fetching S3 bucket resource details for #{bucket_name}..."
|
293
|
-
|
294
|
-
response = `convox api get /resources/#{bucket_name}`
|
295
|
-
raise 'convox command failed!' unless $CHILD_STATUS.success?
|
296
|
-
|
297
|
-
bucket_data = JSON.parse(response)
|
298
|
-
s3_url = bucket_data['url']
|
299
|
-
matches = s3_url.match(
|
300
|
-
%r{^s3://(?<access_key_id>[^:]*):(?<secret_access_key>[^@]*)@(?<bucket_name>.*)$}
|
301
|
-
)
|
302
|
-
|
303
|
-
match_keys = %i[access_key_id secret_access_key bucket_name]
|
304
|
-
unless matches && match_keys.all? { |k| matches[k].present? }
|
305
|
-
raise "#{s3_url} is an invalid S3 URL!"
|
306
|
-
end
|
292
|
+
def add_elasticache_cluster
|
293
|
+
write_terraform_template('elasticache')
|
294
|
+
end
|
307
295
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
}
|
296
|
+
def write_terraform_template(name)
|
297
|
+
template_path = File.join(__dir__, "../../terraform/#{name}.tf.erb")
|
298
|
+
unless File.exist?(template_path)
|
299
|
+
raise "Could not find terraform template at: #{template_path}"
|
313
300
|
end
|
301
|
+
|
302
|
+
template = ERB.new(File.read(template_path))
|
303
|
+
template_output = template.result(binding)
|
304
|
+
|
305
|
+
tf_file_path = File.join(rack_dir, "#{name}.tf")
|
306
|
+
logger.debug "Writing terraform config to #{tf_file_path}..."
|
307
|
+
File.open(tf_file_path, 'w') { |f| f.puts template_output }
|
314
308
|
end
|
315
309
|
|
316
|
-
def
|
317
|
-
|
318
|
-
|
319
|
-
|
310
|
+
def apply_terraform_update!
|
311
|
+
logger.info 'Applying terraform update...'
|
312
|
+
command = if ENV['DEBUG_TERRAFORM']
|
313
|
+
'terraform plan'
|
314
|
+
else
|
315
|
+
'terraform apply -auto-approve'
|
316
|
+
end
|
317
|
+
logger.debug "+ #{command}"
|
320
318
|
|
321
|
-
|
322
|
-
|
323
|
-
|
319
|
+
env = {
|
320
|
+
'AWS_ACCESS_KEY_ID' => config.fetch(:aws_access_key_id),
|
321
|
+
'AWS_SECRET_ACCESS_KEY' => config.fetch(:aws_secret_access_key)
|
322
|
+
}
|
323
|
+
Dir.chdir(rack_dir) do
|
324
|
+
system env, command
|
325
|
+
raise 'terraform command failed!' unless $CHILD_STATUS.success?
|
324
326
|
end
|
325
|
-
|
326
|
-
|
327
|
-
bucket_name = s3_bucket_details[:name]
|
328
|
-
|
329
|
-
logger.debug "Looking up existing CORS policy for #{bucket_name}"
|
330
|
-
existing_cors_policy_string =
|
331
|
-
`AWS_ACCESS_KEY_ID=#{access_key_id} \
|
332
|
-
AWS_SECRET_ACCESS_KEY=#{secret_access_key} \
|
333
|
-
aws s3api get-bucket-cors --bucket #{bucket_name} 2>/dev/null`
|
334
|
-
if $CHILD_STATUS.success? && existing_cors_policy_string.present?
|
335
|
-
# Sort all the nested arrays so that the equality operator works
|
336
|
-
existing_cors_policy = JSON.parse(existing_cors_policy_string)
|
337
|
-
cors_policy_json = JSON.parse(cors_policy_string)
|
338
|
-
[existing_cors_policy, cors_policy_json].each do |policy_json|
|
339
|
-
next unless policy_json.is_a?(Hash) && policy_json['CORSRules']
|
340
|
-
|
341
|
-
policy_json['CORSRules'].each do |rule|
|
342
|
-
rule['AllowedHeaders']&.sort!
|
343
|
-
rule['AllowedMethods']&.sort!
|
344
|
-
rule['AllowedOrigins']&.sort!
|
345
|
-
end
|
346
|
-
end
|
327
|
+
end
|
347
328
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
329
|
+
def terraform_state
|
330
|
+
tf_state_file = File.join(rack_dir, 'terraform.tfstate')
|
331
|
+
JSON.parse(File.read(tf_state_file))
|
332
|
+
end
|
333
|
+
|
334
|
+
def terraform_resource(resource_type, resource_name)
|
335
|
+
resource = terraform_state['resources'].find do |resource|
|
336
|
+
resource['type'] == resource_type && resource['name'] == resource_name
|
352
337
|
end
|
338
|
+
return resource if resource
|
353
339
|
|
354
|
-
|
355
|
-
|
340
|
+
raise "Could not find #{resource_type} resource named #{resource_name} in terraform state!"
|
341
|
+
end
|
356
342
|
|
357
|
-
|
343
|
+
def s3_bucket_details
|
344
|
+
require_config(%i[s3_bucket_name])
|
358
345
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
--cors-configuration "file://cors-policy.json"`
|
364
|
-
unless $CHILD_STATUS.success?
|
365
|
-
raise 'Something went wrong while setting the S3 bucket CORS policy!'
|
366
|
-
end
|
346
|
+
s3_bucket = terraform_resource('aws_s3_bucket', 'docs_s3_bucket')
|
347
|
+
bucket_attributes = s3_bucket['instances'][0]['attributes']
|
348
|
+
access_key = terraform_resource('aws_iam_access_key', 'docspring_user_access_key')
|
349
|
+
key_attributes = access_key['instances'][0]['attributes']
|
367
350
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
351
|
+
{
|
352
|
+
access_key_id: key_attributes['id'],
|
353
|
+
secret_access_key: key_attributes['secret'],
|
354
|
+
name: bucket_attributes['bucket']
|
355
|
+
}
|
356
|
+
end
|
357
|
+
|
358
|
+
def rds_details
|
359
|
+
require_config(%i[database_username database_password])
|
360
|
+
|
361
|
+
database = terraform_resource('aws_db_instance', 'rds_database')
|
362
|
+
database_attributes = database['instances'][0]['attributes']
|
363
|
+
|
364
|
+
username = database_attributes['username']
|
365
|
+
password = database_attributes['password']
|
366
|
+
endpoint = database_attributes['endpoint']
|
367
|
+
postgres_url = "postgres://#{username}:#{password}@#{endpoint}/app"
|
368
|
+
{
|
369
|
+
postgres_url: postgres_url
|
370
|
+
}
|
371
|
+
end
|
372
|
+
|
373
|
+
def elasticache_details
|
374
|
+
require_config(%i[s3_bucket_name])
|
375
|
+
|
376
|
+
# Just ensure that the bucket exists in the state
|
377
|
+
cluster = terraform_resource('aws_elasticache_cluster', 'elasticache_cluster')
|
378
|
+
cluster_attributes = cluster['instances'][0]['attributes']
|
379
|
+
cache_node = cluster_attributes['cache_nodes'][0]
|
380
|
+
redis_url = "redis://#{cache_node['address']}:#{cache_node['port']}/0"
|
381
|
+
|
382
|
+
{
|
383
|
+
redis_url: redis_url
|
384
|
+
}
|
372
385
|
end
|
373
386
|
|
374
387
|
def add_docker_registry!
|
@@ -377,7 +390,7 @@ module Convox
|
|
377
390
|
registry_url = config.fetch(:docker_registry_url)
|
378
391
|
|
379
392
|
logger.debug 'Looking up existing Docker registries...'
|
380
|
-
registries_response = `convox api get /registries`
|
393
|
+
registries_response = `convox api get /registries --rack #{config.fetch(:stack_name)}`
|
381
394
|
unless $CHILD_STATUS.success?
|
382
395
|
raise 'Something went wrong while fetching the list of registries!'
|
383
396
|
end
|
@@ -391,36 +404,37 @@ module Convox
|
|
391
404
|
|
392
405
|
logger.info "Adding Docker Registry: #{registry_url}..."
|
393
406
|
logger.info '=> Documentation: ' \
|
394
|
-
'https://docs.convox.com/
|
407
|
+
'https://docs.convox.com/configuration/private-registries/'
|
395
408
|
|
396
409
|
`convox registries add "#{registry_url}" \
|
397
410
|
"#{config.fetch(:docker_registry_username)}" \
|
398
|
-
"#{config.fetch(:docker_registry_password)}"
|
411
|
+
"#{config.fetch(:docker_registry_password)}" \
|
412
|
+
--rack #{config.fetch(:stack_name)}`
|
399
413
|
return if $CHILD_STATUS.success?
|
400
414
|
|
401
415
|
raise "Something went wrong while adding the #{registry_url} registry!"
|
402
416
|
end
|
403
417
|
|
404
418
|
def default_service_domain_name
|
405
|
-
require_config(%i[convox_app_name
|
406
|
-
|
407
|
-
@default_service_domain_name ||= begin
|
408
|
-
convox_domain = convox_rack_data['domain']
|
409
|
-
elb_name_and_region = convox_domain[/([^.]*\.[^.]*)\..*/, 1]
|
410
|
-
unless elb_name_and_region.present?
|
411
|
-
raise 'Something went wrong while parsing the ELB name and region! ' \
|
412
|
-
"(#{elb_name_and_region})"
|
413
|
-
end
|
414
|
-
app = config.fetch(:convox_app_name)
|
415
|
-
service = config.fetch(:default_service)
|
419
|
+
require_config(%i[convox_app_name])
|
416
420
|
|
417
|
-
|
418
|
-
|
419
|
-
|
421
|
+
app_name = config.fetch(:convox_app_name)
|
422
|
+
default_service = config[:default_service] || 'web'
|
423
|
+
|
424
|
+
convox_api_url = terraform_state['outputs']['api']['value']
|
425
|
+
convox_router_host = convox_api_url.split('@').last.sub(/^api\./, '')
|
426
|
+
|
427
|
+
[default_service, app_name, convox_router_host].join('.').downcase
|
420
428
|
end
|
421
429
|
|
422
|
-
def run_convox_command!(cmd, env = {})
|
430
|
+
def run_convox_command!(cmd, env = {}, rack_arg: true)
|
431
|
+
# Always include the rack as an argument, to
|
432
|
+
# make sure that 'convox switch' doesn't affect any commands
|
423
433
|
command = "convox #{cmd}"
|
434
|
+
if rack_arg
|
435
|
+
command = "#{command} --rack #{config.fetch(:stack_name)}"
|
436
|
+
end
|
437
|
+
logger.debug "+ #{command}"
|
424
438
|
system env, command
|
425
439
|
raise "Error running: #{command}" unless $CHILD_STATUS.success?
|
426
440
|
end
|
@@ -38,6 +38,12 @@ module ConvoxInstaller
|
|
38
38
|
{
|
39
39
|
key: :aws_secret_access_key,
|
40
40
|
title: 'AWS Secret Access Key'
|
41
|
+
},
|
42
|
+
# Short random ID used to ensure that resources are always unique
|
43
|
+
{
|
44
|
+
key: :random_id,
|
45
|
+
value: -> { SecureRandom.hex(4) },
|
46
|
+
hidden: true
|
41
47
|
}
|
42
48
|
].freeze
|
43
49
|
|
@@ -76,6 +82,8 @@ module ConvoxInstaller
|
|
76
82
|
|
77
83
|
highline.say 'Please double check all of these configuration details.'
|
78
84
|
|
85
|
+
break if ENV['AUTOSTART_CONVOX_INSTALLATION']
|
86
|
+
|
79
87
|
agree = highline.agree(
|
80
88
|
'Would you like to start the Convox installation?' \
|
81
89
|
" (press 'n' to correct any settings)"
|
@@ -34,6 +34,14 @@ module ConvoxInstaller
|
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
37
|
+
unless command_present? 'terraform'
|
38
|
+
@missing_packages << {
|
39
|
+
name: 'terraform',
|
40
|
+
brew: 'terraform',
|
41
|
+
docs: 'https://learn.hashicorp.com/tutorials/terraform/install-cli'
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
37
45
|
if @missing_packages.any?
|
38
46
|
logger.error 'This script requires the convox and AWS CLI tools.'
|
39
47
|
if OS.mac?
|
@@ -49,12 +57,15 @@ module ConvoxInstaller
|
|
49
57
|
end
|
50
58
|
|
51
59
|
client = Convox::Client.new
|
52
|
-
|
60
|
+
if client.convox_3_cli?
|
61
|
+
logger.debug "=> Convox CLI is version 3.x.x (#{client.cli_version_string})"
|
62
|
+
return
|
63
|
+
end
|
53
64
|
|
54
|
-
logger.error 'This script requires Convox CLI version
|
65
|
+
logger.error 'This script requires Convox CLI version 3.x.x. ' \
|
55
66
|
"Your Convox CLI version is: #{client.cli_version_string}"
|
56
|
-
logger.error
|
57
|
-
'
|
67
|
+
logger.error "Please run 'brew update convox' or follow the instructions " \
|
68
|
+
'at https://docs.convox.com/getting-started/introduction'
|
58
69
|
quit!
|
59
70
|
end
|
60
71
|
|
data/lib/convox_installer.rb
CHANGED
@@ -32,18 +32,24 @@ module ConvoxInstaller
|
|
32
32
|
%w[
|
33
33
|
backup_convox_host_and_rack
|
34
34
|
install_convox
|
35
|
-
|
36
|
-
|
35
|
+
validate_convox_rack_and_write_current!
|
36
|
+
validate_convox_rack_api!
|
37
37
|
convox_rack_data
|
38
38
|
create_convox_app!
|
39
39
|
set_default_app_for_directory!
|
40
|
-
|
41
|
-
|
40
|
+
add_s3_bucket
|
41
|
+
add_rds_database
|
42
|
+
add_elasticache_cluster
|
43
|
+
apply_terraform_update!
|
44
|
+
terraform_state
|
42
45
|
s3_bucket_details
|
46
|
+
elasticache_details
|
47
|
+
rds_details
|
43
48
|
add_docker_registry!
|
44
49
|
default_service_domain_name
|
45
50
|
run_convox_command!
|
46
51
|
logger
|
52
|
+
rack_already_installed?
|
47
53
|
].each do |method|
|
48
54
|
define_method(method) do |*args|
|
49
55
|
client.send(method, *args)
|
@@ -138,7 +138,7 @@ RSpec.describe Convox::Client do
|
|
138
138
|
stack_name: 'convox-test'
|
139
139
|
}
|
140
140
|
)
|
141
|
-
expect(client).to receive(:
|
141
|
+
expect(client).to receive(:write_current).with(
|
142
142
|
'convox-test-697645520.us-west-2.elb.amazonaws.com'
|
143
143
|
)
|
144
144
|
expect(client.validate_convox_auth_and_write_host!).to(
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'convox_installer'
|
4
4
|
|
5
5
|
RSpec.describe ConvoxInstaller::Requirements do
|
6
|
-
let(:convox_cli_version) { '
|
6
|
+
let(:convox_cli_version) { '3.3.4' }
|
7
7
|
|
8
8
|
before do
|
9
9
|
allow_any_instance_of(
|
@@ -45,8 +45,8 @@ RSpec.describe ConvoxInstaller::Requirements do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
context 'with Convox CLI version
|
49
|
-
let(:convox_cli_version) { '
|
48
|
+
context 'with Convox CLI version 20210208170413' do
|
49
|
+
let(:convox_cli_version) { '20210208170413' }
|
50
50
|
|
51
51
|
it 'shows the correct error message and quit' do
|
52
52
|
req = described_class.new
|
@@ -55,11 +55,10 @@ RSpec.describe ConvoxInstaller::Requirements do
|
|
55
55
|
expect(req).to receive(:quit!)
|
56
56
|
|
57
57
|
expect(req.logger).to receive(:error).with(
|
58
|
-
'This script requires Convox CLI version
|
58
|
+
'This script requires Convox CLI version 3.x.x. Your Convox CLI version is: 20210208170413'
|
59
59
|
)
|
60
60
|
expect(req.logger).to receive(:error).with(
|
61
|
-
|
62
|
-
'Convox CLI version: https://docsv2.convox.com/introduction/installation'
|
61
|
+
"Please run 'brew update convox' or follow the instructions at https://docs.convox.com/getting-started/introduction"
|
63
62
|
)
|
64
63
|
|
65
64
|
req.ensure_requirements!
|
@@ -0,0 +1,46 @@
|
|
1
|
+
resource "aws_elasticache_cluster" "elasticache_cluster" {
|
2
|
+
cluster_id = "<%= config.fetch(:stack_name) %>-elasticache-<%= config.fetch(:random_id) %>"
|
3
|
+
engine = "<%= config[:elasticache_engine] || 'redis' %>"
|
4
|
+
engine_version = "<%= config[:elasticache_engine_version] || '6.x' %>"
|
5
|
+
node_type = "<%= config[:elasticache_node_type] || 'cache.t3.medium' %>"
|
6
|
+
num_cache_nodes = <%= config[:elasticache_num_cache_nodes] || 1 %>
|
7
|
+
port = <%= config[:elasticache_port] || 6379 %>
|
8
|
+
|
9
|
+
subnet_group_name = aws_elasticache_subnet_group.elasticache_subnet_group.name
|
10
|
+
security_group_ids = [aws_security_group.elasticache_security_group.id]
|
11
|
+
|
12
|
+
# Workaround for weird engine_version issue where 6.x works for creation, and fails for update
|
13
|
+
# See: https://github.com/hashicorp/terraform-provider-aws/issues/15625#issuecomment-727759811
|
14
|
+
# Fixed in version 3.38.0 of the Terraform AWS provider.
|
15
|
+
lifecycle {
|
16
|
+
ignore_changes = [engine_version]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
resource "aws_elasticache_subnet_group" "elasticache_subnet_group" {
|
21
|
+
name = "<%= config.fetch(:stack_name) %>-elasticache-cluster-subnetgroup-<%= config.fetch(:random_id) %>"
|
22
|
+
subnet_ids = module.system.cluster.subnets
|
23
|
+
}
|
24
|
+
|
25
|
+
resource "aws_security_group" "elasticache_security_group" {
|
26
|
+
name = "<%= config.fetch(:stack_name) %>-elasticache-securitygroup-<%= config.fetch(:random_id) %>"
|
27
|
+
|
28
|
+
description = "Elasticache Security Group (Managed by Terraform)"
|
29
|
+
vpc_id = module.system.cluster.vpc
|
30
|
+
|
31
|
+
# Only Redis in
|
32
|
+
ingress {
|
33
|
+
from_port = 6379
|
34
|
+
to_port = 6379
|
35
|
+
protocol = "tcp"
|
36
|
+
cidr_blocks = ["10.1.0.0/16"]
|
37
|
+
}
|
38
|
+
|
39
|
+
# Allow all outbound traffic
|
40
|
+
egress {
|
41
|
+
from_port = 0
|
42
|
+
to_port = 0
|
43
|
+
protocol = "-1"
|
44
|
+
cidr_blocks = ["0.0.0.0/0"]
|
45
|
+
}
|
46
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
resource "aws_db_instance" "rds_database" {
|
2
|
+
allocated_storage = <%= config[:database_allocated_storage] || 30 %>
|
3
|
+
engine = "<%= config[:database_engine] || 'postgres' %>"
|
4
|
+
engine_version = "<%= config[:database_engine_version] || '14.2' %>"
|
5
|
+
instance_class = "<%= config[:database_instance_class] || 'db.t3.medium' %>"
|
6
|
+
name = "<%= config.fetch(:stack_name).gsub('-', '_') %>_database"
|
7
|
+
identifier = "<%= config.fetch(:stack_name) %>-rds-<%= config.fetch(:random_id) %>"
|
8
|
+
multi_az = <%= config[:database_multi_az] || true %>
|
9
|
+
username = "<%= config.fetch(:database_username) %>"
|
10
|
+
password = "<%= config.fetch(:database_password) %>"
|
11
|
+
|
12
|
+
final_snapshot_identifier = "<%= config.fetch(:stack_name) %>-rds-<%= config.fetch(:random_id) %>-final-snapshot"
|
13
|
+
skip_final_snapshot = false
|
14
|
+
|
15
|
+
db_subnet_group_name = aws_db_subnet_group.rds_subnet_group.name
|
16
|
+
vpc_security_group_ids = [aws_security_group.rds_security_group.id]
|
17
|
+
}
|
18
|
+
|
19
|
+
resource "aws_db_subnet_group" "rds_subnet_group" {
|
20
|
+
name = "<%= config.fetch(:stack_name) %>-rds-subnetgroup-<%= config.fetch(:random_id) %>"
|
21
|
+
subnet_ids = module.system.cluster.subnets
|
22
|
+
}
|
23
|
+
|
24
|
+
resource "aws_security_group" "rds_security_group" {
|
25
|
+
name = "<%= config.fetch(:stack_name) %>-rds-database-securitygroup-<%= config.fetch(:random_id) %>"
|
26
|
+
|
27
|
+
description = "RDS Security Group (Managed by Terraform)"
|
28
|
+
vpc_id = module.system.cluster.vpc
|
29
|
+
|
30
|
+
# Only Postgres in
|
31
|
+
ingress {
|
32
|
+
from_port = 5432
|
33
|
+
to_port = 5432
|
34
|
+
protocol = "tcp"
|
35
|
+
cidr_blocks = ["10.1.0.0/16"]
|
36
|
+
}
|
37
|
+
|
38
|
+
# Allow all outbound traffic
|
39
|
+
egress {
|
40
|
+
from_port = 0
|
41
|
+
to_port = 0
|
42
|
+
protocol = "-1"
|
43
|
+
cidr_blocks = ["0.0.0.0/0"]
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# AWS provider version is 3.33.0
|
2
|
+
# https://registry.terraform.io/providers/hashicorp/aws/3.33.0
|
3
|
+
|
4
|
+
provider "aws" {
|
5
|
+
region = "<%= config[:aws_region] %>"
|
6
|
+
}
|
7
|
+
|
8
|
+
resource "aws_kms_key" "docs_kms_key" {
|
9
|
+
description = "This key is used to encrypt objects in the DocSpring S3 bucket"
|
10
|
+
deletion_window_in_days = 14
|
11
|
+
}
|
12
|
+
|
13
|
+
# Later versions of aws provider (e.g. 4.8.0) use separate resources for
|
14
|
+
# aws_s3_bucket_acl and aws_s3_bucket_cors_configuration.
|
15
|
+
# This will need to be updated in the future.
|
16
|
+
resource "aws_s3_bucket" "docs_s3_bucket" {
|
17
|
+
bucket = "<%= config.fetch(:stack_name) %>-<%= config.fetch(:s3_bucket_name) %>"
|
18
|
+
acl = "private"
|
19
|
+
|
20
|
+
server_side_encryption_configuration {
|
21
|
+
rule {
|
22
|
+
apply_server_side_encryption_by_default {
|
23
|
+
kms_master_key_id = aws_kms_key.docs_kms_key.arn
|
24
|
+
sse_algorithm = "aws:kms"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
<%= config[:s3_bucket_cors_rule] %>
|
30
|
+
}
|
31
|
+
|
32
|
+
resource "aws_iam_user" "docspring_s3_user" {
|
33
|
+
name = "<%= config.fetch(:stack_name) %>-<%= config.fetch(:s3_bucket_name) %>"
|
34
|
+
}
|
35
|
+
|
36
|
+
resource "aws_iam_access_key" "docspring_user_access_key" {
|
37
|
+
user = aws_iam_user.docspring_s3_user.name
|
38
|
+
}
|
39
|
+
|
40
|
+
resource "aws_iam_user_policy" "docspring_user_s3_policy" {
|
41
|
+
name = "docspring_user_s3_policy"
|
42
|
+
user = aws_iam_user.docspring_s3_user.name
|
43
|
+
|
44
|
+
policy = jsonencode({
|
45
|
+
"Version": "2012-10-17",
|
46
|
+
"Statement": [
|
47
|
+
{
|
48
|
+
"Effect": "Allow",
|
49
|
+
"Action": [
|
50
|
+
"s3:PutObject",
|
51
|
+
"s3:PutObjectAcl",
|
52
|
+
"s3:GetObject",
|
53
|
+
"s3:GetObjectAcl",
|
54
|
+
"s3:DeleteObject"
|
55
|
+
],
|
56
|
+
"Resource": [
|
57
|
+
"arn:aws:s3:::<%= config.fetch(:stack_name) %>-<%= config.fetch(:s3_bucket_name) %>/*",
|
58
|
+
]
|
59
|
+
},
|
60
|
+
{
|
61
|
+
"Effect": "Allow",
|
62
|
+
"Action": [
|
63
|
+
"kms:Encrypt",
|
64
|
+
"kms:Decrypt",
|
65
|
+
"kms:ReEncrypt*",
|
66
|
+
"kms:GenerateDataKey*",
|
67
|
+
"kms:DescribeKey"
|
68
|
+
],
|
69
|
+
"Resource": "*"
|
70
|
+
}
|
71
|
+
]
|
72
|
+
})
|
73
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: convox_installer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Form Applications Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -109,6 +109,9 @@ files:
|
|
109
109
|
- spec/lib/convox_installer/config_spec.rb
|
110
110
|
- spec/lib/convox_installer/requirements_spec.rb
|
111
111
|
- spec/spec_helper.rb
|
112
|
+
- terraform/elasticache.tf.erb
|
113
|
+
- terraform/rds.tf.erb
|
114
|
+
- terraform/s3_bucket.tf.erb
|
112
115
|
homepage: https://github.com/FormAPI/convox_installer
|
113
116
|
licenses:
|
114
117
|
- MIT
|