hashicorptools 0.0.3
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 +7 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +119 -0
- data/LICENSE +22 -0
- data/README.md +21 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/bin/ec2_host +7 -0
- data/data/standard-ami.json +48 -0
- data/hashicorptools.gemspec +97 -0
- data/lib/hashicorptools/auto_scaling_group.rb +149 -0
- data/lib/hashicorptools/code_deploy.rb +58 -0
- data/lib/hashicorptools/ec2_utilities.rb +37 -0
- data/lib/hashicorptools/host.rb +114 -0
- data/lib/hashicorptools/packer.rb +129 -0
- data/lib/hashicorptools/terraform.rb +283 -0
- data/lib/hashicorptools/update_launch_configuration.rb +22 -0
- data/lib/hashicorptools/variables.rb +12 -0
- data/lib/hashicorptools.rb +19 -0
- data/spec/hashicorptools_spec.rb +7 -0
- data/spec/spec_helper.rb +29 -0
- metadata +248 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
module Hashicorptools
|
2
|
+
class Host < Thor
|
3
|
+
|
4
|
+
desc 'hosts', 'list running instances'
|
5
|
+
option :environment, required: false
|
6
|
+
option :role, required: false
|
7
|
+
option :name, required: false
|
8
|
+
def hosts
|
9
|
+
ec2 = Aws::EC2::Client.new(region: 'us-east-1')
|
10
|
+
|
11
|
+
resp = ec2.describe_instances(filters: filters)
|
12
|
+
resp.reservations.each do |reservation|
|
13
|
+
reservation.instances.each do |instance|
|
14
|
+
name = instance.tags.find{|t| t.key == 'Name'}.value
|
15
|
+
puts "#{name} #{instance.public_dns_name}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'ssh', 'ssh to the first matching instance'
|
21
|
+
option :environment, required: false
|
22
|
+
option :role, required: false
|
23
|
+
option :name, required: false
|
24
|
+
def ssh(role = '')
|
25
|
+
ec2 = Aws::EC2::Client.new(region: 'us-east-1')
|
26
|
+
|
27
|
+
resp = ec2.describe_instances(filters: filters(role))
|
28
|
+
if resp.reservations.any?
|
29
|
+
instance = resp.reservations.first.instances.first
|
30
|
+
dns = if instance.public_dns_name.present?
|
31
|
+
instance.public_dns_name
|
32
|
+
else
|
33
|
+
instance.private_dns_name
|
34
|
+
end
|
35
|
+
|
36
|
+
exec "ssh #{ssh_user_fragment}#{dns}"
|
37
|
+
else
|
38
|
+
puts "no instances with #{role} role found"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'console', 'run the agra rails console'
|
43
|
+
option :environment, required: false
|
44
|
+
def console
|
45
|
+
ec2 = Aws::EC2::Client.new(region: 'us-east-1')
|
46
|
+
|
47
|
+
bastion_dns = dns_from_reservations(ec2.describe_instances(filters: filters('console', 'maintenance')))
|
48
|
+
agra_console_dns = dns_from_reservations(ec2.describe_instances(filters: filters('console', 'agra')))
|
49
|
+
|
50
|
+
|
51
|
+
if bastion_dns && agra_console_dns
|
52
|
+
exec "ssh -t #{ssh_user_fragment}#{bastion_dns} 'ssh -t #{ssh_user_fragment}#{agra_console_dns}'"
|
53
|
+
else
|
54
|
+
puts "no instances found"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def dns_from_reservations(resp)
|
61
|
+
if resp.reservations.any?
|
62
|
+
instance = resp.reservations.first.instances.first
|
63
|
+
if instance.public_dns_name.present?
|
64
|
+
instance.public_dns_name
|
65
|
+
else
|
66
|
+
instance.private_dns_name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def application_environment
|
73
|
+
if options[:environment].present?
|
74
|
+
options[:environment]
|
75
|
+
elsif ENV['CHANGESPROUT_APP_ENVIRONMENT'].present?
|
76
|
+
ENV['CHANGESPROUT_APP_ENVIRONMENT']
|
77
|
+
else
|
78
|
+
'staging'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def ssh_user_fragment
|
83
|
+
ENV['AWS_SSH_USERNAME'].present? ? "#{ENV['AWS_SSH_USERNAME']}@" : ''
|
84
|
+
end
|
85
|
+
|
86
|
+
def filters(role = '', kind='')
|
87
|
+
filters = []
|
88
|
+
|
89
|
+
filters << {name: 'instance-state-name', values: ['running']}
|
90
|
+
|
91
|
+
if application_environment.present?
|
92
|
+
filters << {name: 'tag:environment', values: [ application_environment ]}
|
93
|
+
end
|
94
|
+
|
95
|
+
if options[:name].present?
|
96
|
+
filters << {name: 'tag:Name', values: [ options[:name] ]}
|
97
|
+
end
|
98
|
+
|
99
|
+
if role.blank?
|
100
|
+
role = options[:role].present?
|
101
|
+
end
|
102
|
+
|
103
|
+
if role.present?
|
104
|
+
filters << {name: 'tag:role', values: [ role ]}
|
105
|
+
end
|
106
|
+
|
107
|
+
if kind.present?
|
108
|
+
filters << {name: 'tag:kind', values: [ kind ]}
|
109
|
+
end
|
110
|
+
|
111
|
+
filters
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Hashicorptools
|
2
|
+
NUMBER_OF_AMIS_TO_KEEP = 2
|
3
|
+
|
4
|
+
class Packer < Thor
|
5
|
+
include Ec2Utilities
|
6
|
+
include Variables
|
7
|
+
|
8
|
+
desc "build", "creates an AMI from the current config"
|
9
|
+
option :debug, :required => false
|
10
|
+
def build
|
11
|
+
_build
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "validate", "validates the packer config"
|
15
|
+
def validate
|
16
|
+
system "packer validate #{ami_config_path}"
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "console", "interactive session"
|
20
|
+
def console
|
21
|
+
require 'byebug'
|
22
|
+
byebug
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "list", "list all available telize amis"
|
26
|
+
def list
|
27
|
+
amis.each do |ami|
|
28
|
+
puts ami.image_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "clean", "clean old AMIs that are no longer needed"
|
33
|
+
def clean
|
34
|
+
clean_amis
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "boot", "start up an instance of the latest version of AMI"
|
38
|
+
def boot
|
39
|
+
run_instances_resp = ec2.run_instances(image_id: current_ami('base-image').image_id,
|
40
|
+
min_count: 1,
|
41
|
+
max_count: 1,
|
42
|
+
instance_type: "t2.micro")
|
43
|
+
|
44
|
+
ec2.create_tags( resources: run_instances_resp.instances.collect{|i| i.instance_id },
|
45
|
+
tags: [ {key: 'Name', value: "packer test boot #{tag_name}"}, {key: 'environment', value: 'packer-development'}, {key: 'temporary', value: 'kill me'}])
|
46
|
+
|
47
|
+
require 'byebug'
|
48
|
+
byebug
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def _build(settings_overrides={})
|
54
|
+
settings_overrides.merge!({source_ami: source_ami_id, ami_tag: tag_name, cookbook_name: cookbook_name})
|
55
|
+
|
56
|
+
if options[:debug]
|
57
|
+
puts "[DEBUG] Executing 'packer build -debug #{variables(settings_overrides)} #{ami_config_path}'"
|
58
|
+
system "packer build -debug \
|
59
|
+
#{variables(settings_overrides)} \
|
60
|
+
#{ami_config_path}"
|
61
|
+
else
|
62
|
+
system "packer build \
|
63
|
+
#{variables(settings_overrides)} \
|
64
|
+
#{ami_config_path}"
|
65
|
+
end
|
66
|
+
|
67
|
+
clean_amis
|
68
|
+
end
|
69
|
+
|
70
|
+
def source_ami_id
|
71
|
+
current_ami('base-image').image_id
|
72
|
+
end
|
73
|
+
|
74
|
+
def tag_name
|
75
|
+
raise 'implement me'
|
76
|
+
end
|
77
|
+
|
78
|
+
def cookbook_name
|
79
|
+
raise 'implement me'
|
80
|
+
end
|
81
|
+
|
82
|
+
def ami_config_path
|
83
|
+
datadir_path = Gem.datadir('hashicorptools')
|
84
|
+
File.join(datadir_path, 'standard-ami.json')
|
85
|
+
end
|
86
|
+
|
87
|
+
def auto_scaling
|
88
|
+
@auto_scaling ||= Aws::AutoScaling::Client.new(region: 'us-east-1')
|
89
|
+
end
|
90
|
+
|
91
|
+
def ec2_v2
|
92
|
+
@ec2 ||= Aws::EC2::Client.new(region: 'us-east-1')
|
93
|
+
end
|
94
|
+
|
95
|
+
def amis_in_use
|
96
|
+
launch_configs = auto_scaling.describe_launch_configurations
|
97
|
+
image_ids = launch_configs.data['launch_configurations'].collect{|lc| lc.image_id}.flatten
|
98
|
+
|
99
|
+
ec2_reservations = ec2_v2.describe_instances
|
100
|
+
image_ids << ec2_reservations.reservations.collect{|res| res.instances.collect{|r| r.image_id}}.flatten
|
101
|
+
image_ids.flatten
|
102
|
+
end
|
103
|
+
|
104
|
+
def clean_amis
|
105
|
+
ami_ids = amis.collect{|a| a.image_id}
|
106
|
+
ami_ids_to_remove = ami_ids - amis_in_use
|
107
|
+
potential_amis_to_remove = amis
|
108
|
+
potential_amis_to_remove.keep_if {|a| ami_ids_to_remove.include?(a.image_id) }
|
109
|
+
|
110
|
+
if potential_amis_to_remove.size > NUMBER_OF_AMIS_TO_KEEP
|
111
|
+
amis_to_remove = potential_amis_to_remove[NUMBER_OF_AMIS_TO_KEEP..-1]
|
112
|
+
amis_to_keep = potential_amis_to_remove[0..(NUMBER_OF_AMIS_TO_KEEP-1)]
|
113
|
+
|
114
|
+
puts "Deregistering old AMIs..."
|
115
|
+
amis_to_remove.each do |ami|
|
116
|
+
puts "Deregistering #{ami.image_id}"
|
117
|
+
ami.deregister
|
118
|
+
end
|
119
|
+
|
120
|
+
puts "Currently active AMIs..."
|
121
|
+
amis_to_keep.each do |ami|
|
122
|
+
puts ami.image_id
|
123
|
+
end
|
124
|
+
else
|
125
|
+
puts "no AMIs to clean."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
module Hashicorptools
|
2
|
+
class Terraform < Thor
|
3
|
+
TERRAFORM_VERSION = '0.6.8'
|
4
|
+
|
5
|
+
include Ec2Utilities
|
6
|
+
include Variables
|
7
|
+
|
8
|
+
desc 'bootstrap', 'terraform a new infrastructure from scratch'
|
9
|
+
option :environment, :required => true
|
10
|
+
def bootstrap
|
11
|
+
apply
|
12
|
+
end
|
13
|
+
|
14
|
+
[:apply, :plan, :destroy, :pull, :refresh].each do |cmd|
|
15
|
+
desc cmd, "terraform #{cmd}"
|
16
|
+
option :environment, :required => true
|
17
|
+
option :debug, :required => false
|
18
|
+
|
19
|
+
define_method cmd do
|
20
|
+
send("_#{cmd}")
|
21
|
+
end
|
22
|
+
|
23
|
+
no_commands do
|
24
|
+
define_method "_#{cmd}" do |settings_overrides = {}|
|
25
|
+
enforce_version!
|
26
|
+
raise 'invalid environment' unless ['staging', 'production'].include?(options[:environment])
|
27
|
+
|
28
|
+
settings_overrides
|
29
|
+
.merge!({ app_environment: options[:environment] }
|
30
|
+
.merge(env_variable_keys)
|
31
|
+
.merge(settings)
|
32
|
+
.merge(shared_plan_variables))
|
33
|
+
|
34
|
+
decrypt_file(state_path)
|
35
|
+
decrypt_file(var_file_path)
|
36
|
+
|
37
|
+
|
38
|
+
begin
|
39
|
+
send("before_#{cmd}")
|
40
|
+
|
41
|
+
terraform_command = "terraform #{cmd} #{variables(settings_overrides)} -state #{state_path} #{var_file_param} #{config_directory}"
|
42
|
+
|
43
|
+
if (options[:debug])
|
44
|
+
puts "[DEBUG] running command: '#{terraform_command}"
|
45
|
+
end
|
46
|
+
if system terraform_command
|
47
|
+
send("after_#{cmd}")
|
48
|
+
end
|
49
|
+
rescue StandardError => e
|
50
|
+
puts e.message
|
51
|
+
puts e.backtrace
|
52
|
+
ensure
|
53
|
+
# need to always ensure the most recent tfstate is encrypted again.
|
54
|
+
encrypt_file(state_path)
|
55
|
+
delete_decrypted_var_file
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method "before_#{cmd}" do
|
61
|
+
# no-op
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
no_commands do
|
66
|
+
define_method "after_#{cmd}" do
|
67
|
+
# no-op
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
desc cmd, "terraform #{cmd} for shared plan"
|
72
|
+
define_method "shared_#{cmd}" do
|
73
|
+
enforce_version!
|
74
|
+
|
75
|
+
decrypt_file(shared_state_path)
|
76
|
+
|
77
|
+
begin
|
78
|
+
system "terraform #{cmd} #{variables(env_variable_keys.merge(settings))} -state #{shared_state_path} #{shared_config_directory}"
|
79
|
+
rescue StandardError => e
|
80
|
+
puts e.message
|
81
|
+
puts e.backtrace
|
82
|
+
ensure
|
83
|
+
# need to always ensure the most recent tfstate is encrypted again.
|
84
|
+
encrypt_file(shared_state_path)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
[:shared_apply, :shared_plan, :shared_destroy, :shared_pull, :shared_refresh].each do |cmd|
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'output', 'terraform output'
|
94
|
+
option :environment, :required => true
|
95
|
+
option :name, :required => true
|
96
|
+
def output
|
97
|
+
system output_cmd(state_path, options[:name])
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'taint', 'terraform taint'
|
101
|
+
option :environment, :required => true
|
102
|
+
option :name, :required => true
|
103
|
+
def taint
|
104
|
+
system "terraform taint -state #{state_path} #{options[:name]}"
|
105
|
+
end
|
106
|
+
|
107
|
+
desc 'show', 'terraform show'
|
108
|
+
option :environment, :required => true
|
109
|
+
def show
|
110
|
+
system "terraform show #{state_path}"
|
111
|
+
end
|
112
|
+
|
113
|
+
[ {commands: [:decrypt, :encrypt], file_path_method: :state_path, desc: 'upstream terraform changes'},
|
114
|
+
{commands: [:shared_decrypt, :shared_encrypt], file_path_method: :shared_state_path, desc: 'upstream shared terraform changes'},
|
115
|
+
{commands: [:var_file_decrypt, :var_file_encrypt], file_path_method: :var_file_path, desc: 'tfvars file'} ].each do |crypto_commands|
|
116
|
+
|
117
|
+
desc crypto_commands[:commands][0], "decrypt #{crypto_commands[:desc]}"
|
118
|
+
option :environment, :required => true
|
119
|
+
define_method crypto_commands[:commands][0] do
|
120
|
+
file_path = send(crypto_commands[:file_path_method])
|
121
|
+
decrypt_file(file_path)
|
122
|
+
end
|
123
|
+
|
124
|
+
desc crypto_commands[:commands][1], "encrypt #{crypto_commands[:desc]}"
|
125
|
+
option :environment, :required => true
|
126
|
+
define_method crypto_commands[:commands][1] do
|
127
|
+
file_path = send(crypto_commands[:file_path_method])
|
128
|
+
encrypt_file(file_path)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
desc "console", "interactive session"
|
133
|
+
def console
|
134
|
+
require 'pry-byebug'
|
135
|
+
binding.pry
|
136
|
+
end
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
140
|
+
def var_file_param
|
141
|
+
File.exist?(var_file_path) ?
|
142
|
+
"-var-file #{var_file_path}" :
|
143
|
+
""
|
144
|
+
end
|
145
|
+
|
146
|
+
def delete_decrypted_var_file
|
147
|
+
return unless File.exist?(var_file_path)
|
148
|
+
|
149
|
+
enforce_cryptography_dependencies
|
150
|
+
if !File.exist?("#{var_file_path}.enc")
|
151
|
+
encrypt_file(var_file_path)
|
152
|
+
end
|
153
|
+
|
154
|
+
File.delete(var_file_path)
|
155
|
+
end
|
156
|
+
|
157
|
+
def encrypt_file(file_path)
|
158
|
+
enforce_cryptography_dependencies
|
159
|
+
if File.exist?(file_path)
|
160
|
+
system "openssl enc -aes-256-cbc -salt -in #{file_path} -out #{file_path}.enc -k #{ENV['TFSTATE_ENCRYPTION_PASSWORD']}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def decrypt_file(file_path, enforce_file_existence=false)
|
165
|
+
enforce_cryptography_dependencies
|
166
|
+
if File.exist?("#{file_path}.enc")
|
167
|
+
system "openssl enc -aes-256-cbc -d -in #{file_path}.enc -out #{file_path} -k #{ENV['TFSTATE_ENCRYPTION_PASSWORD']}"
|
168
|
+
elsif enforce_file_existence
|
169
|
+
raise "Could not find #{file_path}.enc"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def state_path
|
174
|
+
"#{config_environment_path}/#{options[:environment]}.tfstate"
|
175
|
+
end
|
176
|
+
|
177
|
+
def shared_state_path
|
178
|
+
"#{shared_config_directory}/shared.tfstate"
|
179
|
+
end
|
180
|
+
|
181
|
+
def var_file_path
|
182
|
+
"#{config_environment_path}/variables.tfvars"
|
183
|
+
end
|
184
|
+
|
185
|
+
def config_directory
|
186
|
+
"config/infrastructure/#{infrastructure}"
|
187
|
+
end
|
188
|
+
|
189
|
+
def shared_config_directory
|
190
|
+
"config/infrastructure/#{infrastructure}/shared"
|
191
|
+
end
|
192
|
+
|
193
|
+
def config_environment_path
|
194
|
+
"#{config_directory}/environments/#{options[:environment]}"
|
195
|
+
end
|
196
|
+
|
197
|
+
def infrastructure
|
198
|
+
raise 'implement me'
|
199
|
+
end
|
200
|
+
|
201
|
+
def output_cmd(state_file_path, name=nil)
|
202
|
+
"terraform output -state=#{state_file_path} #{name}"
|
203
|
+
end
|
204
|
+
|
205
|
+
def output_variable(state_file_path, name)
|
206
|
+
`#{output_cmd(state_file_path, name)}`.chomp
|
207
|
+
end
|
208
|
+
|
209
|
+
def output_variables(state_file_path)
|
210
|
+
raw_plan_output = `#{output_cmd(state_file_path)}`
|
211
|
+
output_vars = {}
|
212
|
+
raw_plan_output.split("\n").each do |output_var|
|
213
|
+
key, value = output_var.split("=")
|
214
|
+
output_vars[key.strip] = value.strip
|
215
|
+
end
|
216
|
+
|
217
|
+
output_vars
|
218
|
+
end
|
219
|
+
|
220
|
+
def terraform_version
|
221
|
+
version_string = `terraform version`.chomp
|
222
|
+
version = /(\d+.\d+.\d+)/.match(version_string)
|
223
|
+
version[0]
|
224
|
+
end
|
225
|
+
|
226
|
+
def enforce_version!
|
227
|
+
if Gem::Version.new(terraform_version) < Gem::Version.new(TERRAFORM_VERSION)
|
228
|
+
raise "Terraform #{terraform_version} is out of date, please upgrade"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def enforce_cryptography_dependencies
|
233
|
+
raise "must supply TFSTATE_ENCRYPTION_PASSWORD environmental variable" if ENV['TFSTATE_ENCRYPTION_PASSWORD'].blank?
|
234
|
+
end
|
235
|
+
|
236
|
+
def settings
|
237
|
+
{} # override me to pass more variables into the terraform plan.
|
238
|
+
end
|
239
|
+
|
240
|
+
def asg_launch_config_name(asg_name)
|
241
|
+
asg_client = Aws::AutoScaling::Client.new(region: 'us-east-1')
|
242
|
+
group = asg_client.describe_auto_scaling_groups(auto_scaling_group_names: [asg_name]).auto_scaling_groups.first
|
243
|
+
group.try(:launch_configuration_name)
|
244
|
+
end
|
245
|
+
|
246
|
+
def env_variable_keys
|
247
|
+
{} # override me to pass environmental variables into the terraform plan
|
248
|
+
end
|
249
|
+
|
250
|
+
def shared_plan_variables
|
251
|
+
decrypt_file(shared_state_path, false)
|
252
|
+
if File.exist?(shared_state_path)
|
253
|
+
output_variables(shared_state_path)
|
254
|
+
else
|
255
|
+
{}
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def fetch_terraform_modules
|
260
|
+
system "terraform get -update=true #{config_directory}"
|
261
|
+
end
|
262
|
+
|
263
|
+
def current_tfstate
|
264
|
+
return @current_tfstate if defined?(@current_tfstate)
|
265
|
+
raw_conf = File.read(state_path)
|
266
|
+
@current_tfstate = JSON.parse(raw_conf)
|
267
|
+
end
|
268
|
+
|
269
|
+
def read_config_file(path)
|
270
|
+
File.new('config/' + path).read
|
271
|
+
template = ERB.new File.new("config/#{path}").read, nil, "%"
|
272
|
+
template.result(OpenStruct.new(options).instance_eval { binding })
|
273
|
+
end
|
274
|
+
|
275
|
+
def dynect
|
276
|
+
@dynect ||= DynectRest.new("controlshiftlabs", ENV['DYNECT_USERNAME'], ENV['DYNECT_PASSWORD'], "controlshiftlabs.com")
|
277
|
+
end
|
278
|
+
|
279
|
+
def dns_record_exists?(parent_node_fqdn, record)
|
280
|
+
dynect.node_list(nil, parent_node_fqdn).include?(record.fqdn)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Hashicorptools
|
2
|
+
class UpdateLaunchConfiguration < Thor
|
3
|
+
desc 'deploy ASG_NAME', 'recycle instances in the ASG with no downtime'
|
4
|
+
def deploy(asg_name)
|
5
|
+
asg = AutoScalingGroup.new(name: asg_name)
|
6
|
+
if asg.group.nil?
|
7
|
+
raise "could not find asg #{asg_name}"
|
8
|
+
end
|
9
|
+
current_count = asg.group.instances.size || 1
|
10
|
+
|
11
|
+
if asg.group.max_size < (current_count * 2)
|
12
|
+
raise "max size must be more than twice current count to deploy a new AMI"
|
13
|
+
else
|
14
|
+
# first doulbe the instance count to get new launch config live.
|
15
|
+
asg.set_desired_instances(current_count * 2)
|
16
|
+
|
17
|
+
# then bring the instance count back down again.
|
18
|
+
asg.set_desired_instances(current_count)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Hashicorptools
|
2
|
+
module Variables
|
3
|
+
def aws_credentials_settings(settings_overrides = {})
|
4
|
+
{aws_access_key: ENV['AWS_ACCESS_KEY_ID'],
|
5
|
+
aws_secret_key: ENV['AWS_SECRET_ACCESS_KEY']}.merge(settings_overrides)
|
6
|
+
end
|
7
|
+
|
8
|
+
def variables(settings_overrides = {})
|
9
|
+
aws_credentials_settings(settings_overrides).collect{|key,value| "-var '#{key}=#{value}'" }.join(' ')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'dotenv'
|
3
|
+
require 'thor'
|
4
|
+
require 'active_support/all'
|
5
|
+
require 'aws-sdk-v1'
|
6
|
+
require 'aws-sdk'
|
7
|
+
require 'dynect_rest'
|
8
|
+
|
9
|
+
module Hashicorptools
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative 'hashicorptools/variables'
|
13
|
+
require_relative 'hashicorptools/ec2_utilities'
|
14
|
+
require_relative 'hashicorptools/auto_scaling_group'
|
15
|
+
require_relative 'hashicorptools/packer'
|
16
|
+
require_relative 'hashicorptools/terraform'
|
17
|
+
require_relative 'hashicorptools/host'
|
18
|
+
require_relative 'hashicorptools/update_launch_configuration'
|
19
|
+
require_relative 'hashicorptools/code_deploy'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
module SimpleCov::Configuration
|
4
|
+
def clean_filters
|
5
|
+
@filters = []
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
SimpleCov.configure do
|
10
|
+
clean_filters
|
11
|
+
load_adapter 'test_frameworks'
|
12
|
+
end
|
13
|
+
|
14
|
+
ENV["COVERAGE"] && SimpleCov.start do
|
15
|
+
add_filter "/.rvm/"
|
16
|
+
end
|
17
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
18
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
19
|
+
|
20
|
+
require 'rspec'
|
21
|
+
require 'hashicorptools'
|
22
|
+
|
23
|
+
# Requires supporting files with custom matchers and macros, etc,
|
24
|
+
# in ./support/ and its subdirectories.
|
25
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
26
|
+
|
27
|
+
RSpec.configure do |config|
|
28
|
+
|
29
|
+
end
|