convox_installer 1.0.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -2
- data/.vscode/settings.json +3 -2
- data/Gemfile +3 -2
- data/README.md +112 -39
- data/convox_installer.gemspec +18 -17
- data/examples/full_installation.rb +85 -78
- data/lib/convox/client.rb +278 -225
- data/lib/convox.rb +1 -1
- data/lib/convox_installer/config.rb +43 -30
- data/lib/convox_installer/requirements.rb +38 -18
- data/lib/convox_installer/version.rb +1 -1
- data/lib/convox_installer.rb +16 -10
- data/spec/lib/convox/client_spec.rb +92 -56
- data/spec/lib/convox_installer/config_spec.rb +140 -140
- data/spec/lib/convox_installer/requirements_spec.rb +62 -34
- data/spec/spec_helper.rb +1 -1
- data/terraform/elasticache.tf.erb +46 -0
- data/terraform/rds.tf.erb +45 -0
- data/terraform/s3_bucket.tf.erb +73 -0
- metadata +23 -19
@@ -1,48 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require 'highline'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'json'
|
6
|
+
require 'securerandom'
|
7
7
|
|
8
8
|
module ConvoxInstaller
|
9
9
|
class Config
|
10
|
-
CONFIG_FILE = File.expand_path(
|
10
|
+
CONFIG_FILE = File.expand_path('./.installer_config.json').freeze
|
11
11
|
|
12
12
|
attr_accessor :logger, :config, :prompts, :highline
|
13
13
|
|
14
14
|
DEFAULT_PROMPTS = [
|
15
15
|
{
|
16
16
|
key: :stack_name,
|
17
|
-
title:
|
18
|
-
prompt:
|
19
|
-
default:
|
17
|
+
title: 'Convox Stack Name',
|
18
|
+
prompt: 'Please enter a name for your Convox installation',
|
19
|
+
default: 'convox'
|
20
20
|
},
|
21
21
|
{
|
22
22
|
key: :aws_region,
|
23
|
-
title:
|
24
|
-
default:
|
23
|
+
title: 'AWS Region',
|
24
|
+
default: 'us-east-1'
|
25
25
|
},
|
26
26
|
{
|
27
27
|
key: :instance_type,
|
28
|
-
title:
|
29
|
-
default:
|
28
|
+
title: 'EC2 Instance Type',
|
29
|
+
default: 't3.medium'
|
30
30
|
},
|
31
31
|
{
|
32
|
-
section:
|
32
|
+
section: 'Admin AWS Credentials'
|
33
33
|
},
|
34
34
|
{
|
35
35
|
key: :aws_access_key_id,
|
36
|
-
title:
|
36
|
+
title: 'AWS Access Key ID'
|
37
37
|
},
|
38
38
|
{
|
39
39
|
key: :aws_secret_access_key,
|
40
|
-
title:
|
40
|
+
title: 'AWS Secret Access Key'
|
41
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
|
47
|
+
}
|
42
48
|
].freeze
|
43
49
|
|
44
50
|
def initialize(options = {})
|
45
|
-
@logger = Logger.new(
|
51
|
+
@logger = Logger.new($stdout)
|
46
52
|
logger.level = options[:log_level] || Logger::INFO
|
47
53
|
|
48
54
|
self.prompts = options[:prompts] || DEFAULT_PROMPTS
|
@@ -74,13 +80,16 @@ module ConvoxInstaller
|
|
74
80
|
|
75
81
|
@completed_prompt = true
|
76
82
|
|
77
|
-
highline.say
|
83
|
+
highline.say 'Please double check all of these configuration details.'
|
84
|
+
|
85
|
+
break if ENV['AUTOSTART_CONVOX_INSTALLATION']
|
78
86
|
|
79
87
|
agree = highline.agree(
|
80
|
-
|
88
|
+
'Would you like to start the Convox installation?' \
|
81
89
|
" (press 'n' to correct any settings)"
|
82
90
|
)
|
83
91
|
break if agree
|
92
|
+
|
84
93
|
highline.say "\n"
|
85
94
|
end
|
86
95
|
|
@@ -89,7 +98,7 @@ module ConvoxInstaller
|
|
89
98
|
|
90
99
|
def show_config_summary
|
91
100
|
highline.say "\n============================================"
|
92
|
-
highline.say
|
101
|
+
highline.say ' SUMMARY'
|
93
102
|
highline.say "============================================\n\n"
|
94
103
|
|
95
104
|
config_titles = prompts.map do |prompt|
|
@@ -106,10 +115,18 @@ module ConvoxInstaller
|
|
106
115
|
highline.say " #{padded_key} #{value}"
|
107
116
|
end
|
108
117
|
highline.say "\nWe've saved your configuration to: #{CONFIG_FILE}"
|
109
|
-
highline.say
|
118
|
+
highline.say 'If anything goes wrong during the installation, ' \
|
110
119
|
"you can restart the script to reload the config and continue.\n\n"
|
111
120
|
end
|
112
121
|
|
122
|
+
def self.config_file_exists?
|
123
|
+
File.exist?(CONFIG_FILE)
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.read_config_file
|
127
|
+
File.read(CONFIG_FILE)
|
128
|
+
end
|
129
|
+
|
113
130
|
private
|
114
131
|
|
115
132
|
def ask_prompt(prompt)
|
@@ -129,7 +146,11 @@ module ConvoxInstaller
|
|
129
146
|
return if config[key]
|
130
147
|
|
131
148
|
default = prompt[:value]
|
132
|
-
config[key] = default.is_a?(Proc)
|
149
|
+
config[key] = if default.is_a?(Proc)
|
150
|
+
default.arity.zero? ? default.call : default.call(config)
|
151
|
+
else
|
152
|
+
default
|
153
|
+
end
|
133
154
|
save_config_to_file
|
134
155
|
return
|
135
156
|
end
|
@@ -153,7 +174,7 @@ module ConvoxInstaller
|
|
153
174
|
|
154
175
|
logger.debug "Loading saved config from #{CONFIG_FILE}..."
|
155
176
|
|
156
|
-
loaded_config = JSON.parse(Config.read_config_file)[
|
177
|
+
loaded_config = JSON.parse(Config.read_config_file)['config'].symbolize_keys
|
157
178
|
self.config = config.merge(loaded_config)
|
158
179
|
end
|
159
180
|
|
@@ -174,13 +195,5 @@ module ConvoxInstaller
|
|
174
195
|
f.puts(JSON.pretty_generate(config: config))
|
175
196
|
end
|
176
197
|
end
|
177
|
-
|
178
|
-
def self.config_file_exists?
|
179
|
-
File.exist?(CONFIG_FILE)
|
180
|
-
end
|
181
|
-
|
182
|
-
def self.read_config_file
|
183
|
-
File.read(CONFIG_FILE)
|
184
|
-
end
|
185
198
|
end
|
186
199
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'highline'
|
4
|
+
require 'os'
|
5
|
+
require 'logger'
|
6
6
|
|
7
7
|
module ConvoxInstaller
|
8
8
|
class Requirements
|
@@ -10,46 +10,66 @@ module ConvoxInstaller
|
|
10
10
|
|
11
11
|
def initialize(options = {})
|
12
12
|
@ecr_label = options[:ecr_label]
|
13
|
-
@logger = Logger.new(
|
13
|
+
@logger = Logger.new($stdout)
|
14
14
|
logger.level = options[:log_level] || Logger::INFO
|
15
15
|
end
|
16
16
|
|
17
17
|
def ensure_requirements!
|
18
|
-
logger.debug
|
18
|
+
logger.debug 'Checking for required commands...'
|
19
19
|
|
20
20
|
@missing_packages = []
|
21
|
-
unless
|
21
|
+
unless command_present? 'convox'
|
22
22
|
@missing_packages << {
|
23
|
-
name:
|
24
|
-
brew:
|
25
|
-
docs:
|
23
|
+
name: 'convox',
|
24
|
+
brew: 'convox',
|
25
|
+
docs: 'https://docs.convox.com/introduction/installation'
|
26
26
|
}
|
27
27
|
end
|
28
28
|
|
29
|
-
unless
|
29
|
+
unless command_present? 'aws'
|
30
30
|
@missing_packages << {
|
31
|
-
name:
|
32
|
-
brew:
|
33
|
-
docs:
|
31
|
+
name: 'aws',
|
32
|
+
brew: 'awscli',
|
33
|
+
docs: 'https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html'
|
34
|
+
}
|
35
|
+
end
|
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'
|
34
42
|
}
|
35
43
|
end
|
36
44
|
|
37
45
|
if @missing_packages.any?
|
38
|
-
logger.error
|
46
|
+
logger.error 'This script requires the convox and AWS CLI tools.'
|
39
47
|
if OS.mac?
|
40
|
-
logger.error
|
41
|
-
"#{@missing_packages.map { |p| p[:brew] }.join(
|
48
|
+
logger.error 'Please run: brew install ' \
|
49
|
+
"#{@missing_packages.map { |p| p[:brew] }.join(' ')}"
|
42
50
|
else
|
43
|
-
logger.error
|
51
|
+
logger.error 'Installation Instructions:'
|
44
52
|
@missing_packages.each do |package|
|
45
53
|
logger.error "* #{package[:name]}: #{package[:docs]}"
|
46
54
|
end
|
47
55
|
end
|
48
56
|
quit!
|
49
57
|
end
|
58
|
+
|
59
|
+
client = Convox::Client.new
|
60
|
+
if client.convox_3_cli?
|
61
|
+
logger.debug "=> Convox CLI is version 3.x.x (#{client.cli_version_string})"
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
logger.error 'This script requires Convox CLI version 3.x.x. ' \
|
66
|
+
"Your Convox CLI version is: #{client.cli_version_string}"
|
67
|
+
logger.error "Please run 'brew update convox' or follow the instructions " \
|
68
|
+
'at https://docs.convox.com/getting-started/introduction'
|
69
|
+
quit!
|
50
70
|
end
|
51
71
|
|
52
|
-
def
|
72
|
+
def command_present?(command)
|
53
73
|
path = find_command command
|
54
74
|
if path.present?
|
55
75
|
logger.debug "=> Found #{command}: #{path}"
|
data/lib/convox_installer.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext'
|
5
|
+
require 'convox_installer/config'
|
6
|
+
require 'convox_installer/requirements'
|
7
|
+
require 'convox'
|
8
8
|
|
9
9
|
module ConvoxInstaller
|
10
10
|
def client
|
@@ -12,7 +12,7 @@ module ConvoxInstaller
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def config
|
15
|
-
options = {log_level: @log_level}
|
15
|
+
options = { log_level: @log_level }
|
16
16
|
options[:prompts] = @prompts if @prompts
|
17
17
|
@config ||= Config.new(options)
|
18
18
|
end
|
@@ -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)
|
@@ -1,20 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'convox/client'
|
4
4
|
|
5
5
|
RSpec.describe Convox::Client do
|
6
|
-
let(:home_dir) { File.expand_path(
|
6
|
+
let(:home_dir) { File.expand_path('~') }
|
7
7
|
|
8
|
-
it
|
8
|
+
it 'finds the authentication details in ~/.convox/auth' do
|
9
9
|
expect(File).to receive(:exist?).with("#{home_dir}/.convox/auth").and_return(true)
|
10
10
|
expect(File).to receive(:read).with("#{home_dir}/.convox/auth").and_return(
|
11
11
|
'{ "test.example.com": "1234567890" }'
|
12
12
|
)
|
13
13
|
client = described_class.new
|
14
|
-
expect(client.auth).to eq(
|
14
|
+
expect(client.auth).to eq('test.example.com' => '1234567890')
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
describe 'Convox CLI version' do
|
18
|
+
let(:client) { described_class.new }
|
19
|
+
|
20
|
+
it 'returns the convox CLI version output for 20210208170413' do
|
21
|
+
expect(client).to receive(:cli_version_string).at_least(:once).and_return('20210208170413')
|
22
|
+
expect(client.convox_2_cli?).to be true
|
23
|
+
expect(client.convox_3_cli?).to be false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns the convox CLI version output for 20200101130413' do
|
27
|
+
expect(client).to receive(:cli_version_string).at_least(:once).and_return('20200101130413')
|
28
|
+
expect(client.convox_2_cli?).to be true
|
29
|
+
expect(client.convox_3_cli?).to be false
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns the convox CLI version output for 3.0.0' do
|
33
|
+
expect(client).to receive(:cli_version_string).at_least(:once).and_return('3.0.0')
|
34
|
+
expect(client.convox_2_cli?).to be false
|
35
|
+
expect(client.convox_3_cli?).to be true
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns the convox CLI version output for 3.1.3' do
|
39
|
+
expect(client).to receive(:cli_version_string).at_least(:once).and_return('3.1.3')
|
40
|
+
expect(client.convox_2_cli?).to be false
|
41
|
+
expect(client.convox_3_cli?).to be true
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns the convox CLI version output for 4.0.0' do
|
45
|
+
expect(client).to receive(:cli_version_string).at_least(:once).and_return('4.0.0')
|
46
|
+
expect(client.convox_2_cli?).to be false
|
47
|
+
expect(client.convox_3_cli?).to be false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'backups existing Convox host and rack files' do
|
18
52
|
expect(File).to receive(:exist?).with(
|
19
53
|
"#{home_dir}/.convox/host"
|
20
54
|
).and_return(true)
|
@@ -34,61 +68,63 @@ RSpec.describe Convox::Client do
|
|
34
68
|
client.backup_convox_host_and_rack
|
35
69
|
end
|
36
70
|
|
37
|
-
describe
|
38
|
-
it
|
71
|
+
describe '#install' do
|
72
|
+
it 'requires the correct config vars' do
|
39
73
|
client = described_class.new
|
40
|
-
expect { client.install_convox }.to raise_error(
|
74
|
+
expect { client.install_convox }.to raise_error('aws_region is missing from the config!')
|
41
75
|
|
42
|
-
client = described_class.new(config: {aws_region:
|
43
|
-
expect { client.install_convox }.to raise_error(
|
76
|
+
client = described_class.new(config: { aws_region: 'us-east-1' })
|
77
|
+
expect { client.install_convox }.to raise_error('stack_name is missing from the config!')
|
44
78
|
end
|
45
79
|
|
46
|
-
it
|
80
|
+
it 'runs the correct convox CLI command' do
|
47
81
|
client = described_class.new(
|
48
82
|
config: {
|
49
|
-
aws_region:
|
50
|
-
aws_access_key_id:
|
51
|
-
aws_secret_access_key:
|
52
|
-
stack_name:
|
53
|
-
instance_type:
|
54
|
-
}
|
83
|
+
aws_region: 'us-east-1',
|
84
|
+
aws_access_key_id: 'asdf',
|
85
|
+
aws_secret_access_key: '1234',
|
86
|
+
stack_name: 'asdf',
|
87
|
+
instance_type: 't3.medium'
|
88
|
+
}
|
55
89
|
)
|
56
90
|
|
57
91
|
expect(client.logger).to receive(:info)
|
58
92
|
expect(client).to receive(:run_convox_command!).with(
|
59
|
-
|
60
|
-
"
|
61
|
-
|
62
|
-
|
63
|
-
|
93
|
+
'rack install aws --name "asdf" "InstanceType=t3.medium" ' \
|
94
|
+
'"BuildInstance="',
|
95
|
+
'AWS_ACCESS_KEY_ID' => 'asdf',
|
96
|
+
'AWS_REGION' => 'us-east-1',
|
97
|
+
'AWS_SECRET_ACCESS_KEY' => '1234'
|
64
98
|
)
|
65
99
|
client.install_convox
|
66
100
|
end
|
67
101
|
end
|
68
102
|
|
69
|
-
describe
|
70
|
-
it
|
103
|
+
describe '#validate_convox_auth_and_write_host!' do
|
104
|
+
it 'requires the correct config vars' do
|
71
105
|
client = described_class.new
|
72
|
-
expect
|
106
|
+
expect do
|
107
|
+
client.validate_convox_auth_and_write_host!
|
108
|
+
end.to raise_error('aws_region is missing from the config!')
|
73
109
|
end
|
74
110
|
|
75
|
-
it
|
111
|
+
it 'raises an error if auth file is missing' do
|
76
112
|
client = described_class.new(
|
77
113
|
config: {
|
78
|
-
aws_region:
|
79
|
-
stack_name:
|
80
|
-
}
|
114
|
+
aws_region: 'us-east-1',
|
115
|
+
stack_name: 'asdf'
|
116
|
+
}
|
81
117
|
)
|
82
118
|
expect(File).to receive(:exist?).with(
|
83
119
|
"#{home_dir}/.convox/auth"
|
84
120
|
).and_return(false)
|
85
121
|
|
86
|
-
expect
|
87
|
-
client.
|
88
|
-
|
122
|
+
expect do
|
123
|
+
client.validate_convox_auth_and_write_host!
|
124
|
+
end.to raise_error(/Could not find auth file at /)
|
89
125
|
end
|
90
126
|
|
91
|
-
it
|
127
|
+
it 'sets ~/.convox/host if a matching host is found in the auth file' do
|
92
128
|
expect(File).to receive(:exist?).with(
|
93
129
|
"#{home_dir}/.convox/auth"
|
94
130
|
).twice.and_return(true)
|
@@ -98,19 +134,19 @@ RSpec.describe Convox::Client do
|
|
98
134
|
)
|
99
135
|
client = described_class.new(
|
100
136
|
config: {
|
101
|
-
aws_region:
|
102
|
-
stack_name:
|
103
|
-
}
|
137
|
+
aws_region: 'us-west-2',
|
138
|
+
stack_name: 'convox-test'
|
139
|
+
}
|
104
140
|
)
|
105
|
-
expect(client).to receive(:
|
106
|
-
|
141
|
+
expect(client).to receive(:write_current).with(
|
142
|
+
'convox-test-697645520.us-west-2.elb.amazonaws.com'
|
107
143
|
)
|
108
|
-
expect(client.
|
109
|
-
eq(
|
144
|
+
expect(client.validate_convox_auth_and_write_host!).to(
|
145
|
+
eq('convox-test-697645520.us-west-2.elb.amazonaws.com')
|
110
146
|
)
|
111
147
|
end
|
112
148
|
|
113
|
-
it
|
149
|
+
it 'raises an error if no matching host is found' do
|
114
150
|
expect(File).to receive(:exist?).with(
|
115
151
|
"#{home_dir}/.convox/auth"
|
116
152
|
).twice.and_return(true)
|
@@ -120,17 +156,17 @@ RSpec.describe Convox::Client do
|
|
120
156
|
)
|
121
157
|
client = described_class.new(
|
122
158
|
config: {
|
123
|
-
aws_region:
|
124
|
-
stack_name:
|
125
|
-
}
|
159
|
+
aws_region: 'us-east-1',
|
160
|
+
stack_name: 'convox-test'
|
161
|
+
}
|
126
162
|
)
|
127
|
-
expect
|
128
|
-
client.
|
129
|
-
|
130
|
-
|
163
|
+
expect do
|
164
|
+
client.validate_convox_auth_and_write_host!
|
165
|
+
end.to raise_error('Could not find matching authentication for ' \
|
166
|
+
'region: us-east-1, stack: convox-test')
|
131
167
|
end
|
132
168
|
|
133
|
-
it
|
169
|
+
it 'raises an error if it finds multiple matching hosts' do
|
134
170
|
expect(File).to receive(:exist?).with(
|
135
171
|
"#{home_dir}/.convox/auth"
|
136
172
|
).twice.and_return(true)
|
@@ -141,14 +177,14 @@ RSpec.describe Convox::Client do
|
|
141
177
|
)
|
142
178
|
client = described_class.new(
|
143
179
|
config: {
|
144
|
-
aws_region:
|
145
|
-
stack_name:
|
146
|
-
}
|
180
|
+
aws_region: 'us-west-2',
|
181
|
+
stack_name: 'convox-test'
|
182
|
+
}
|
147
183
|
)
|
148
|
-
expect
|
149
|
-
client.
|
150
|
-
|
151
|
-
|
184
|
+
expect do
|
185
|
+
client.validate_convox_auth_and_write_host!
|
186
|
+
end.to raise_error('Found multiple matching hosts for ' \
|
187
|
+
'region: us-west-2, stack: convox-test')
|
152
188
|
end
|
153
189
|
end
|
154
190
|
end
|