vagrant-gq 0.1.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 +7 -0
- data/.gitignore +21 -0
- data/Gemfile +14 -0
- data/LICENSE +22 -0
- data/README.md +169 -0
- data/Rakefile +21 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/greenqloud-generic.box +0 -0
- data/lib/vagrant-gq/action/connect_gq.rb +47 -0
- data/lib/vagrant-gq/action/is_created.rb +18 -0
- data/lib/vagrant-gq/action/is_stopped.rb +18 -0
- data/lib/vagrant-gq/action/message_already_created.rb +16 -0
- data/lib/vagrant-gq/action/message_not_created.rb +16 -0
- data/lib/vagrant-gq/action/message_will_not_destroy.rb +16 -0
- data/lib/vagrant-gq/action/read_ssh_info.rb +53 -0
- data/lib/vagrant-gq/action/read_state.rb +38 -0
- data/lib/vagrant-gq/action/run_instance.rb +247 -0
- data/lib/vagrant-gq/action/start_instance.rb +81 -0
- data/lib/vagrant-gq/action/stop_instance.rb +28 -0
- data/lib/vagrant-gq/action/sync_folders.rb +118 -0
- data/lib/vagrant-gq/action/terminate_instance.rb +47 -0
- data/lib/vagrant-gq/action/timed_provision.rb +21 -0
- data/lib/vagrant-gq/action/wait_for_state.rb +41 -0
- data/lib/vagrant-gq/action/warn_networks.rb +19 -0
- data/lib/vagrant-gq/action.rb +190 -0
- data/lib/vagrant-gq/config.rb +372 -0
- data/lib/vagrant-gq/errors.rb +31 -0
- data/lib/vagrant-gq/plugin.rb +73 -0
- data/lib/vagrant-gq/provider.rb +50 -0
- data/lib/vagrant-gq/util/timer.rb +17 -0
- data/lib/vagrant-gq/version.rb +5 -0
- data/lib/vagrant-gq.rb +18 -0
- data/locales/en.yml +122 -0
- data/spec/vagrant-gq/config_spec.rb +216 -0
- data/vagrant-gq.gemspec +58 -0
- metadata +148 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require 'vagrant/util/retryable'
|
5
|
+
|
6
|
+
require 'vagrant-gq/util/timer'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module GQ
|
10
|
+
module Action
|
11
|
+
# This runs the configured instance.
|
12
|
+
class RunInstance
|
13
|
+
include Vagrant::Util::Retryable
|
14
|
+
|
15
|
+
def initialize(app, env)
|
16
|
+
@app = app
|
17
|
+
@logger = Log4r::Logger.new("vagrant_gq::action::run_instance")
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
# Initialize metrics if they haven't been
|
22
|
+
env[:metrics] ||= {}
|
23
|
+
|
24
|
+
# Get the region we're going to booting up in
|
25
|
+
region = env[:machine].provider_config.region
|
26
|
+
|
27
|
+
# Get the configs
|
28
|
+
region_config = env[:machine].provider_config.get_region_config(region)
|
29
|
+
ami = region_config.qmi
|
30
|
+
availability_zone = region_config.availability_zone
|
31
|
+
instance_type = region_config.instance_type
|
32
|
+
keypair = region_config.keypair_name
|
33
|
+
private_ip_address = region_config.private_ip_address
|
34
|
+
security_groups = region_config.security_groups
|
35
|
+
subnet_id = region_config.subnet_id
|
36
|
+
tags = region_config.tags
|
37
|
+
user_data = region_config.user_data
|
38
|
+
block_device_mapping = region_config.block_device_mapping
|
39
|
+
elastic_ip = region_config.elastic_ip
|
40
|
+
terminate_on_shutdown = region_config.terminate_on_shutdown
|
41
|
+
iam_instance_profile_arn = region_config.iam_instance_profile_arn
|
42
|
+
iam_instance_profile_name = region_config.iam_instance_profile_name
|
43
|
+
monitoring = region_config.monitoring
|
44
|
+
ebs_optimized = region_config.ebs_optimized
|
45
|
+
associate_public_ip = region_config.associate_public_ip
|
46
|
+
|
47
|
+
# If there is no keypair then warn the user
|
48
|
+
if !keypair
|
49
|
+
env[:ui].warn(I18n.t("vagrant_gq.launch_no_keypair"))
|
50
|
+
end
|
51
|
+
|
52
|
+
# If there is a subnet ID then warn the user
|
53
|
+
if subnet_id && !elastic_ip
|
54
|
+
env[:ui].warn(I18n.t("vagrant_gq.launch_vpc_warning"))
|
55
|
+
end
|
56
|
+
|
57
|
+
# Launch!
|
58
|
+
env[:ui].info(I18n.t("vagrant_gq.launching_instance"))
|
59
|
+
env[:ui].info(" -- Type: #{instance_type}")
|
60
|
+
env[:ui].info(" -- AMI: #{ami}")
|
61
|
+
env[:ui].info(" -- Region: #{region}")
|
62
|
+
env[:ui].info(" -- Availability Zone: #{availability_zone}") if availability_zone
|
63
|
+
env[:ui].info(" -- Keypair: #{keypair}") if keypair
|
64
|
+
env[:ui].info(" -- Subnet ID: #{subnet_id}") if subnet_id
|
65
|
+
env[:ui].info(" -- IAM Instance Profile ARN: #{iam_instance_profile_arn}") if iam_instance_profile_arn
|
66
|
+
env[:ui].info(" -- IAM Instance Profile Name: #{iam_instance_profile_name}") if iam_instance_profile_name
|
67
|
+
env[:ui].info(" -- Private IP: #{private_ip_address}") if private_ip_address
|
68
|
+
env[:ui].info(" -- Elastic IP: #{elastic_ip}") if elastic_ip
|
69
|
+
env[:ui].info(" -- User Data: yes") if user_data
|
70
|
+
env[:ui].info(" -- Security Groups: #{security_groups.inspect}") if !security_groups.empty?
|
71
|
+
env[:ui].info(" -- User Data: #{user_data}") if user_data
|
72
|
+
env[:ui].info(" -- Block Device Mapping: #{block_device_mapping}") if block_device_mapping
|
73
|
+
env[:ui].info(" -- Terminate On Shutdown: #{terminate_on_shutdown}")
|
74
|
+
env[:ui].info(" -- Monitoring: #{monitoring}")
|
75
|
+
env[:ui].info(" -- EBS optimized: #{ebs_optimized}")
|
76
|
+
env[:ui].info(" -- Assigning a public IP address in a VPC: #{associate_public_ip}")
|
77
|
+
|
78
|
+
options = {
|
79
|
+
:availability_zone => availability_zone,
|
80
|
+
:flavor_id => instance_type,
|
81
|
+
:image_id => ami,
|
82
|
+
:key_name => keypair,
|
83
|
+
:private_ip_address => private_ip_address,
|
84
|
+
:subnet_id => subnet_id,
|
85
|
+
:iam_instance_profile_arn => iam_instance_profile_arn,
|
86
|
+
:iam_instance_profile_name => iam_instance_profile_name,
|
87
|
+
:tags => tags,
|
88
|
+
:user_data => user_data,
|
89
|
+
:block_device_mapping => block_device_mapping,
|
90
|
+
:instance_initiated_shutdown_behavior => terminate_on_shutdown == true ? "terminate" : nil,
|
91
|
+
:monitoring => monitoring,
|
92
|
+
:ebs_optimized => ebs_optimized,
|
93
|
+
:associate_public_ip => associate_public_ip
|
94
|
+
}
|
95
|
+
if !security_groups.empty?
|
96
|
+
security_group_key = options[:subnet_id].nil? ? :groups : :security_group_ids
|
97
|
+
options[security_group_key] = security_groups
|
98
|
+
end
|
99
|
+
|
100
|
+
begin
|
101
|
+
env[:ui].warn(I18n.t("vagrant_gq.warn_ssh_access")) unless allows_ssh_port?(env, security_groups, subnet_id)
|
102
|
+
|
103
|
+
server = env[:gq_compute].servers.create(options)
|
104
|
+
rescue Fog::Compute::GQ::NotFound => e
|
105
|
+
# Invalid subnet doesn't have its own error so we catch and
|
106
|
+
# check the error message here.
|
107
|
+
if e.message =~ /subnet ID/
|
108
|
+
raise Errors::FogError,
|
109
|
+
:message => "Subnet ID not found: #{subnet_id}"
|
110
|
+
end
|
111
|
+
|
112
|
+
raise
|
113
|
+
rescue Fog::Compute::GQ::Error => e
|
114
|
+
raise Errors::FogError, :message => e.message
|
115
|
+
rescue Excon::Errors::HTTPStatusError => e
|
116
|
+
raise Errors::InternalFogError,
|
117
|
+
:error => e.message,
|
118
|
+
:response => e.response.body
|
119
|
+
end
|
120
|
+
|
121
|
+
# Immediately save the ID since it is created at this point.
|
122
|
+
env[:machine].id = server.id
|
123
|
+
|
124
|
+
# Wait for the instance to be ready first
|
125
|
+
env[:metrics]["instance_ready_time"] = Util::Timer.time do
|
126
|
+
tries = region_config.instance_ready_timeout / 2
|
127
|
+
|
128
|
+
env[:ui].info(I18n.t("vagrant_gq.waiting_for_ready"))
|
129
|
+
begin
|
130
|
+
retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
|
131
|
+
# If we're interrupted don't worry about waiting
|
132
|
+
next if env[:interrupted]
|
133
|
+
|
134
|
+
# Wait for the server to be ready
|
135
|
+
server.wait_for(2) { ready? }
|
136
|
+
end
|
137
|
+
rescue Fog::Errors::TimeoutError
|
138
|
+
# Delete the instance
|
139
|
+
terminate(env)
|
140
|
+
|
141
|
+
# Notify the user
|
142
|
+
raise Errors::InstanceReadyTimeout,
|
143
|
+
timeout: region_config.instance_ready_timeout
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
|
148
|
+
|
149
|
+
# Allocate and associate an elastic IP if requested
|
150
|
+
if elastic_ip
|
151
|
+
domain = subnet_id ? 'vpc' : 'standard'
|
152
|
+
do_elastic_ip(env, domain, server)
|
153
|
+
end
|
154
|
+
|
155
|
+
if !env[:interrupted]
|
156
|
+
env[:metrics]["instance_ssh_time"] = Util::Timer.time do
|
157
|
+
# Wait for SSH to be ready.
|
158
|
+
env[:ui].info(I18n.t("vagrant_gq.waiting_for_ssh"))
|
159
|
+
while true
|
160
|
+
# If we're interrupted then just back out
|
161
|
+
break if env[:interrupted]
|
162
|
+
break if env[:machine].communicate.ready?
|
163
|
+
sleep 2
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
@logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
|
168
|
+
|
169
|
+
# Ready and booted!
|
170
|
+
env[:ui].info(I18n.t("vagrant_gq.ready"))
|
171
|
+
end
|
172
|
+
|
173
|
+
# Terminate the instance if we were interrupted
|
174
|
+
terminate(env) if env[:interrupted]
|
175
|
+
|
176
|
+
@app.call(env)
|
177
|
+
end
|
178
|
+
|
179
|
+
def recover(env)
|
180
|
+
return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
|
181
|
+
|
182
|
+
if env[:machine].provider.state.id != :not_created
|
183
|
+
# Undo the import
|
184
|
+
terminate(env)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def allows_ssh_port?(env, test_sec_groups, is_vpc)
|
189
|
+
port = 22 # TODO get ssh_info port
|
190
|
+
test_sec_groups = [ "default" ] if test_sec_groups.empty? # GQ default security group
|
191
|
+
# filter groups by name or group_id (vpc)
|
192
|
+
groups = test_sec_groups.map do |tsg|
|
193
|
+
env[:gq_compute].security_groups.all.select { |sg| tsg == (is_vpc ? sg.group_id : sg.name) }
|
194
|
+
end.flatten
|
195
|
+
# filter TCP rules
|
196
|
+
rules = groups.map { |sg| sg.ip_permissions.select { |r| r["ipProtocol"] == "tcp" } }.flatten
|
197
|
+
# test if any range includes port
|
198
|
+
!rules.select { |r| (r["fromPort"]..r["toPort"]).include?(port) }.empty?
|
199
|
+
end
|
200
|
+
|
201
|
+
def do_elastic_ip(env, domain, server)
|
202
|
+
begin
|
203
|
+
allocation = env[:gq_compute].allocate_address(domain)
|
204
|
+
rescue
|
205
|
+
@logger.debug("Could not allocate Elastic IP.")
|
206
|
+
terminate(env)
|
207
|
+
raise Errors::FogError,
|
208
|
+
:message => "Could not allocate Elastic IP."
|
209
|
+
end
|
210
|
+
@logger.debug("Public IP #{allocation.body['publicIp']}")
|
211
|
+
|
212
|
+
# Associate the address and save the metadata to a hash
|
213
|
+
if domain == 'vpc'
|
214
|
+
# VPC requires an allocation ID to assign an IP
|
215
|
+
association = env[:gq_compute].associate_address(server.id, nil, nil, allocation.body['allocationId'])
|
216
|
+
h = { :allocation_id => allocation.body['allocationId'], :association_id => association.body['associationId'], :public_ip => allocation.body['publicIp'] }
|
217
|
+
else
|
218
|
+
# Standard EC2 instances only need the allocated IP address
|
219
|
+
association = env[:gq_compute].associate_address(server.id, allocation.body['publicIp'])
|
220
|
+
h = { :public_ip => allocation.body['publicIp'] }
|
221
|
+
end
|
222
|
+
|
223
|
+
unless association.body['return']
|
224
|
+
@logger.debug("Could not associate Elastic IP.")
|
225
|
+
terminate(env)
|
226
|
+
raise Errors::FogError,
|
227
|
+
:message => "Could not allocate Elastic IP."
|
228
|
+
end
|
229
|
+
|
230
|
+
# Save this IP to the data dir so it can be released when the instance is destroyed
|
231
|
+
ip_file = env[:machine].data_dir.join('elastic_ip')
|
232
|
+
ip_file.open('w+') do |f|
|
233
|
+
f.write(h.to_json)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def terminate(env)
|
238
|
+
destroy_env = env.dup
|
239
|
+
destroy_env.delete(:interrupted)
|
240
|
+
destroy_env[:config_validate] = false
|
241
|
+
destroy_env[:force_confirm_destroy] = true
|
242
|
+
env[:action_runner].run(Action.action_destroy, destroy_env)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
require 'vagrant/util/retryable'
|
4
|
+
|
5
|
+
require 'vagrant-gq/util/timer'
|
6
|
+
|
7
|
+
module VagrantPlugins
|
8
|
+
module GQ
|
9
|
+
module Action
|
10
|
+
# This starts a stopped instance.
|
11
|
+
class StartInstance
|
12
|
+
include Vagrant::Util::Retryable
|
13
|
+
|
14
|
+
def initialize(app, env)
|
15
|
+
@app = app
|
16
|
+
@logger = Log4r::Logger.new("vagrant_gq::action::start_instance")
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
# Initialize metrics if they haven't been
|
21
|
+
env[:metrics] ||= {}
|
22
|
+
|
23
|
+
server = env[:gq_compute].servers.get(env[:machine].id)
|
24
|
+
|
25
|
+
env[:ui].info(I18n.t("vagrant_gq.starting"))
|
26
|
+
|
27
|
+
begin
|
28
|
+
server.start
|
29
|
+
|
30
|
+
region = env[:machine].provider_config.region
|
31
|
+
region_config = env[:machine].provider_config.get_region_config(region)
|
32
|
+
|
33
|
+
# Wait for the instance to be ready first
|
34
|
+
env[:metrics]["instance_ready_time"] = Util::Timer.time do
|
35
|
+
tries = region_config.instance_ready_timeout / 2
|
36
|
+
|
37
|
+
env[:ui].info(I18n.t("vagrant_gq.waiting_for_ready"))
|
38
|
+
begin
|
39
|
+
retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
|
40
|
+
# If we're interrupted don't worry about waiting
|
41
|
+
next if env[:interrupted]
|
42
|
+
|
43
|
+
# Wait for the server to be ready
|
44
|
+
server.wait_for(2) { ready? }
|
45
|
+
end
|
46
|
+
rescue Fog::Errors::TimeoutError
|
47
|
+
# Notify the user
|
48
|
+
raise Errors::InstanceReadyTimeout,
|
49
|
+
timeout: region_config.instance_ready_timeout
|
50
|
+
end
|
51
|
+
end
|
52
|
+
rescue Fog::Compute::GQ::Error => e
|
53
|
+
raise Errors::FogError, :message => e.message
|
54
|
+
end
|
55
|
+
|
56
|
+
@logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
|
57
|
+
|
58
|
+
if !env[:interrupted]
|
59
|
+
env[:metrics]["instance_ssh_time"] = Util::Timer.time do
|
60
|
+
# Wait for SSH to be ready.
|
61
|
+
env[:ui].info(I18n.t("vagrant_gq.waiting_for_ssh"))
|
62
|
+
while true
|
63
|
+
# If we're interrupted then just back out
|
64
|
+
break if env[:interrupted]
|
65
|
+
break if env[:machine].communicate.ready?
|
66
|
+
sleep 2
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
|
71
|
+
|
72
|
+
# Ready and booted!
|
73
|
+
env[:ui].info(I18n.t("vagrant_gq.ready"))
|
74
|
+
end
|
75
|
+
|
76
|
+
@app.call(env)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module GQ
|
5
|
+
module Action
|
6
|
+
# This stops the running instance.
|
7
|
+
class StopInstance
|
8
|
+
def initialize(app, env)
|
9
|
+
@app = app
|
10
|
+
@logger = Log4r::Logger.new("vagrant_gq::action::stop_instance")
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
server = env[:gq_compute].servers.get(env[:machine].id)
|
15
|
+
|
16
|
+
if env[:machine].state.id == :stopped
|
17
|
+
env[:ui].info(I18n.t("vagrant_gq.already_status", :status => env[:machine].state.id))
|
18
|
+
else
|
19
|
+
env[:ui].info(I18n.t("vagrant_gq.stopping"))
|
20
|
+
server.stop(!!env[:force_halt])
|
21
|
+
end
|
22
|
+
|
23
|
+
@app.call(env)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
require "vagrant/util/subprocess"
|
4
|
+
|
5
|
+
require "vagrant/util/scoped_hash_override"
|
6
|
+
|
7
|
+
require "vagrant/util/which"
|
8
|
+
|
9
|
+
module VagrantPlugins
|
10
|
+
module GQ
|
11
|
+
module Action
|
12
|
+
# This middleware uses `rsync` to sync the folders over to the
|
13
|
+
# GQ instance.
|
14
|
+
class SyncFolders
|
15
|
+
include Vagrant::Util::ScopedHashOverride
|
16
|
+
|
17
|
+
def initialize(app, env)
|
18
|
+
@app = app
|
19
|
+
@logger = Log4r::Logger.new("vagrant_gq::action::sync_folders")
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
@app.call(env)
|
24
|
+
|
25
|
+
ssh_info = env[:machine].ssh_info
|
26
|
+
|
27
|
+
unless Vagrant::Util::Which.which('rsync')
|
28
|
+
env[:ui].warn(I18n.t('vagrant_gq.rsync_not_found_warning', :side => "host"))
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
if env[:machine].communicate.execute('which rsync', :error_check => false) != 0
|
33
|
+
env[:ui].warn(I18n.t('vagrant_gq.rsync_not_found_warning', :side => "guest"))
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
env[:machine].config.vm.synced_folders.each do |id, data|
|
38
|
+
data = scoped_hash_override(data, :gq)
|
39
|
+
|
40
|
+
# Ignore disabled shared folders
|
41
|
+
next if data[:disabled]
|
42
|
+
|
43
|
+
hostpath = File.expand_path(data[:hostpath], env[:root_path])
|
44
|
+
guestpath = data[:guestpath]
|
45
|
+
|
46
|
+
# Make sure there is a trailing slash on the host path to
|
47
|
+
# avoid creating an additional directory with rsync
|
48
|
+
hostpath = "#{hostpath}/" if hostpath !~ /\/$/
|
49
|
+
|
50
|
+
# on windows rsync.exe requires cygdrive-style paths
|
51
|
+
if Vagrant::Util::Platform.windows?
|
52
|
+
hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" }
|
53
|
+
end
|
54
|
+
|
55
|
+
env[:ui].info(I18n.t("vagrant_gq.rsync_folder",
|
56
|
+
:hostpath => hostpath,
|
57
|
+
:guestpath => guestpath))
|
58
|
+
|
59
|
+
# Create the host path if it doesn't exist and option flag is set
|
60
|
+
if data[:create]
|
61
|
+
begin
|
62
|
+
FileUtils::mkdir_p(hostpath)
|
63
|
+
rescue => err
|
64
|
+
raise Errors::MkdirError,
|
65
|
+
:hostpath => hostpath,
|
66
|
+
:err => err
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create the guest path
|
71
|
+
env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
|
72
|
+
env[:machine].communicate.sudo(
|
73
|
+
"chown -R #{ssh_info[:username]} '#{guestpath}'")
|
74
|
+
|
75
|
+
#collect rsync excludes specified :rsync_excludes=>['path1',...] in synced_folder options
|
76
|
+
excludes = ['.vagrant/', 'Vagrantfile', *Array(data[:rsync_excludes])].uniq
|
77
|
+
|
78
|
+
ssh_options = ["StrictHostKeyChecking=no"]
|
79
|
+
# Use proxy command if it's set
|
80
|
+
if ssh_info[:proxy_command]
|
81
|
+
ssh_options.push("ProxyCommand #{ssh_info[:proxy_command]}")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Rsync over to the guest path using the SSH info
|
85
|
+
command = [
|
86
|
+
"rsync", "--verbose", "--archive", "-z",
|
87
|
+
*excludes.map{|e|['--exclude', e]}.flatten,
|
88
|
+
"-e", "ssh -p #{ssh_info[:port]} #{ssh_key_options(ssh_info)} " +
|
89
|
+
ssh_options.map { |ssh_option| "-o '#{ssh_option}' " }.join,
|
90
|
+
hostpath,
|
91
|
+
"#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
|
92
|
+
|
93
|
+
# we need to fix permissions when using rsync.exe on windows, see
|
94
|
+
# http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions
|
95
|
+
if Vagrant::Util::Platform.windows?
|
96
|
+
command.insert(1, "--chmod", "ugo=rwX")
|
97
|
+
end
|
98
|
+
|
99
|
+
r = Vagrant::Util::Subprocess.execute(*command)
|
100
|
+
if r.exit_code != 0
|
101
|
+
raise Errors::RsyncError,
|
102
|
+
:guestpath => guestpath,
|
103
|
+
:hostpath => hostpath,
|
104
|
+
:stderr => r.stderr
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def ssh_key_options(ssh_info)
|
112
|
+
# Ensure that `private_key_path` is an Array (for Vagrant < 1.4)
|
113
|
+
Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module GQ
|
6
|
+
module Action
|
7
|
+
# This terminates the running instance.
|
8
|
+
class TerminateInstance
|
9
|
+
def initialize(app, env)
|
10
|
+
@app = app
|
11
|
+
@logger = Log4r::Logger.new("vagrant_gq::action::terminate_instance")
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
server = env[:gq_compute].servers.get(env[:machine].id)
|
16
|
+
|
17
|
+
# Release the elastic IP
|
18
|
+
ip_file = env[:machine].data_dir.join('elastic_ip')
|
19
|
+
if ip_file.file?
|
20
|
+
release_address(env,ip_file.read)
|
21
|
+
ip_file.delete
|
22
|
+
end
|
23
|
+
|
24
|
+
# Destroy the server and remove the tracking ID
|
25
|
+
env[:ui].info(I18n.t("vagrant_gq.terminating"))
|
26
|
+
server.destroy
|
27
|
+
env[:machine].id = nil
|
28
|
+
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Release an elastic IP address
|
33
|
+
def release_address(env,eip)
|
34
|
+
h = JSON.parse(eip)
|
35
|
+
# Use association_id and allocation_id for VPC, use public IP for EC2
|
36
|
+
if h['association_id']
|
37
|
+
env[:gq_compute].disassociate_address(nil,h['association_id'])
|
38
|
+
env[:gq_compute].release_address(h['allocation_id'])
|
39
|
+
else
|
40
|
+
env[:gq_compute].disassociate_address(h['public_ip'])
|
41
|
+
env[:gq_compute].release_address(h['public_ip'])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "vagrant-gq/util/timer"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module GQ
|
5
|
+
module Action
|
6
|
+
# This is the same as the builtin provision except it times the
|
7
|
+
# provisioner runs.
|
8
|
+
class TimedProvision < Vagrant::Action::Builtin::Provision
|
9
|
+
def run_provisioner(env, name, p)
|
10
|
+
timer = Util::Timer.time do
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
env[:metrics] ||= {}
|
15
|
+
env[:metrics]["provisioner_times"] ||= []
|
16
|
+
env[:metrics]["provisioner_times"] << [name, timer]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require "timeout"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module GQ
|
6
|
+
module Action
|
7
|
+
# This action will wait for a machine to reach a specific state or quit by timeout
|
8
|
+
class WaitForState
|
9
|
+
# env[:result] will be false in case of timeout.
|
10
|
+
# @param [Symbol] state Target machine state.
|
11
|
+
# @param [Number] timeout Timeout in seconds.
|
12
|
+
def initialize(app, env, state, timeout)
|
13
|
+
@app = app
|
14
|
+
@logger = Log4r::Logger.new("vagrant_gq::action::wait_for_state")
|
15
|
+
@state = state
|
16
|
+
@timeout = timeout
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
env[:result] = true
|
21
|
+
if env[:machine].state.id == @state
|
22
|
+
@logger.info(I18n.t("vagrant_gq.already_status", :status => @state))
|
23
|
+
else
|
24
|
+
@logger.info("Waiting for machine to reach state #{@state}")
|
25
|
+
begin
|
26
|
+
Timeout.timeout(@timeout) do
|
27
|
+
until env[:machine].state.id == @state
|
28
|
+
sleep 2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue Timeout::Error
|
32
|
+
env[:result] = false # couldn't reach state in time
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@app.call(env)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module GQ
|
3
|
+
module Action
|
4
|
+
class WarnNetworks
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
if env[:machine].config.vm.networks.length > 0
|
11
|
+
env[:ui].warn(I18n.t("vagrant_gq.warn_networks"))
|
12
|
+
end
|
13
|
+
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|