boxgrinder-build 0.9.3 → 0.9.4
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.
- data/CHANGELOG +12 -0
- data/Manifest +6 -5
- data/README.md +1 -1
- data/Rakefile +13 -20
- data/boxgrinder-build.gemspec +7 -10
- data/lib/boxgrinder-build/helpers/aws-helper.rb +81 -0
- data/lib/boxgrinder-build/helpers/ec2-helper.rb +182 -0
- data/lib/boxgrinder-build/helpers/guestfs-helper.rb +5 -1
- data/lib/boxgrinder-build/helpers/s3-helper.rb +124 -0
- data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +95 -299
- data/lib/boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin.rb +2 -1
- data/lib/boxgrinder-build/plugins/delivery/s3/s3-plugin.rb +67 -133
- data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +15 -14
- data/rubygem-boxgrinder-build.spec +23 -28
- data/spec/appliance-spec.rb +1 -0
- data/spec/helpers/augeas-helper-spec.rb +1 -0
- data/spec/helpers/ec2-helper-spec.rb +260 -0
- data/spec/helpers/guestfs-helper-spec.rb +34 -7
- data/spec/helpers/image-helper-spec.rb +1 -0
- data/spec/helpers/linux-helper-spec.rb +1 -0
- data/spec/helpers/package-helper-spec.rb +1 -0
- data/spec/helpers/plugin-helper-spec.rb +1 -0
- data/spec/helpers/s3-helper-spec.rb +168 -0
- data/spec/managers/plugin-manager-spec.rb +1 -0
- data/spec/plugins/base-plugin-spec.rb +1 -1
- data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +115 -204
- data/spec/plugins/delivery/elastichosts/elastichosts-plugin-spec.rb +5 -4
- data/spec/plugins/delivery/local/local-plugin-spec.rb +1 -0
- data/spec/plugins/delivery/s3/s3-plugin-spec.rb +143 -134
- data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +1 -0
- data/spec/plugins/os/centos/centos-plugin-spec.rb +1 -0
- data/spec/plugins/os/fedora/fedora-plugin-spec.rb +1 -0
- data/spec/plugins/os/rhel/rhel-plugin-spec.rb +1 -0
- data/spec/plugins/os/rpm-based/kickstart-spec.rb +5 -1
- data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +9 -7
- data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +1 -0
- data/spec/plugins/os/sl/sl-plugin-spec.rb +1 -0
- data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +1 -0
- data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +1 -0
- data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +1 -0
- metadata +17 -23
@@ -0,0 +1,124 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2010 Red Hat, Inc.
|
3
|
+
#
|
4
|
+
# This is free software; you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Lesser General Public License as
|
6
|
+
# published by the Free Software Foundation; either version 3 of
|
7
|
+
# the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This software is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this software; if not, write to the Free
|
16
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
17
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
18
|
+
|
19
|
+
require 'boxgrinder-build/helpers/aws-helper'
|
20
|
+
|
21
|
+
module BoxGrinder
|
22
|
+
class S3Helper < AWSHelper
|
23
|
+
|
24
|
+
#AWS::S3 object should be instantiated already, as config can be inserted
|
25
|
+
#via global AWS.config or via AWS::S3.initialize
|
26
|
+
def initialize(ec2, s3, options={})
|
27
|
+
raise ArgumentError, "ec2 argument must not be nil" if ec2.nil?
|
28
|
+
raise ArgumentError, "s3 argument must not be nil" if s3.nil?
|
29
|
+
@ec2 = ec2
|
30
|
+
@s3 = s3
|
31
|
+
@log = options[:log] || LogHelper.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def bucket(options={})
|
35
|
+
defaults = {:bucket => nil, :acl => :private, :location_constraint => 'us-east-1', :create_if_missing => false}.merge!(options)
|
36
|
+
options = parse_opts(options, defaults)
|
37
|
+
|
38
|
+
s3b = @s3.buckets[options[:bucket]]
|
39
|
+
return s3b if s3b.exists?
|
40
|
+
return @s3.buckets.create(options[:bucket],
|
41
|
+
:acl => options[:acl],
|
42
|
+
:location_constraint => options[:location_constraint]) if options[:create_if_missing]
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
#aws-sdk 1.0.3 added .exists?
|
47
|
+
def object_exists?(s3_object)
|
48
|
+
s3_object.exists?
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete_folder(bucket, path)
|
52
|
+
bucket.objects.with_prefix(deslash(path)).map(&:delete)
|
53
|
+
end
|
54
|
+
|
55
|
+
def stub_s3obj(bucket, path)
|
56
|
+
bucket.objects[path]
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_path(path)
|
60
|
+
return '' if path == '/'
|
61
|
+
#Remove preceding and trailing slashes
|
62
|
+
deslash(path) << '/'
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.endpoints
|
66
|
+
ENDPOINTS
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
#Remove extraneous slashes on paths to ensure they are valid for S3
|
72
|
+
def deslash(path)
|
73
|
+
"#{path.gsub(/^(\/)*/, '').gsub(/(\/)*$/, '')}"
|
74
|
+
end
|
75
|
+
|
76
|
+
ENDPOINTS = {
|
77
|
+
'eu-west-1' => {
|
78
|
+
:endpoint => 's3-eu-west-1.amazonaws.com',
|
79
|
+
:location => 'EU',
|
80
|
+
:kernel => {
|
81
|
+
:i386 => {:aki => 'aki-4deec439'},
|
82
|
+
:x86_64 => {:aki => 'aki-4feec43b'}
|
83
|
+
}
|
84
|
+
},
|
85
|
+
|
86
|
+
'ap-southeast-1' => {
|
87
|
+
:endpoint => 's3-ap-southeast-1.amazonaws.com',
|
88
|
+
:location => 'ap-southeast-1',
|
89
|
+
:kernel => {
|
90
|
+
:i386 => {:aki => 'aki-13d5aa41'},
|
91
|
+
:x86_64 => {:aki => 'aki-11d5aa43'}
|
92
|
+
}
|
93
|
+
},
|
94
|
+
|
95
|
+
'ap-northeast-1' => {
|
96
|
+
:endpoint => 's3-ap-northeast-1.amazonaws.com',
|
97
|
+
:location => 'ap-northeast-1',
|
98
|
+
:kernel => {
|
99
|
+
:i386 => {:aki => 'aki-d209a2d3'},
|
100
|
+
:x86_64 => {:aki => 'aki-d409a2d5'}
|
101
|
+
}
|
102
|
+
},
|
103
|
+
|
104
|
+
'us-west-1' => {
|
105
|
+
:endpoint => 's3-us-west-1.amazonaws.com',
|
106
|
+
:location => 'us-west-1',
|
107
|
+
:kernel => {
|
108
|
+
:i386 => {:aki => 'aki-99a0f1dc'},
|
109
|
+
:x86_64 => {:aki => 'aki-9ba0f1de'}
|
110
|
+
}
|
111
|
+
},
|
112
|
+
|
113
|
+
'us-east-1' => {
|
114
|
+
:endpoint => 's3.amazonaws.com',
|
115
|
+
:location => '',
|
116
|
+
:kernel => {
|
117
|
+
:i386 => {:aki => 'aki-407d9529'},
|
118
|
+
:x86_64 => {:aki => 'aki-427d952b'}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -18,60 +18,14 @@
|
|
18
18
|
|
19
19
|
require 'rubygems'
|
20
20
|
require 'boxgrinder-build/plugins/base-plugin'
|
21
|
-
require '
|
21
|
+
require 'boxgrinder-build/helpers/ec2-helper'
|
22
|
+
require 'aws-sdk'
|
22
23
|
require 'open-uri'
|
23
24
|
require 'timeout'
|
24
25
|
require 'pp'
|
25
26
|
|
26
27
|
module BoxGrinder
|
27
28
|
class EBSPlugin < BasePlugin
|
28
|
-
KERNELS = {
|
29
|
-
'eu-west-1' => {
|
30
|
-
:endpoint => 'ec2.eu-west-1.amazonaws.com',
|
31
|
-
:location => 'EU',
|
32
|
-
:kernel => {
|
33
|
-
'i386' => {:aki => 'aki-4deec439'},
|
34
|
-
'x86_64' => {:aki => 'aki-4feec43b'}
|
35
|
-
}
|
36
|
-
},
|
37
|
-
|
38
|
-
'ap-southeast-1' => {
|
39
|
-
:endpoint => 'ec2.ap-southeast-1.amazonaws.com',
|
40
|
-
:location => 'ap-southeast-1',
|
41
|
-
:kernel => {
|
42
|
-
'i386' => {:aki => 'aki-13d5aa41'},
|
43
|
-
'x86_64' => {:aki => 'aki-11d5aa43'}
|
44
|
-
}
|
45
|
-
},
|
46
|
-
|
47
|
-
'ap-northeast-1' => {
|
48
|
-
:endpoint => 'ec2.ap-northeast-1.amazonaws.com',
|
49
|
-
:location => 'ap-northeast-1',
|
50
|
-
:kernel => {
|
51
|
-
'i386' => {:aki => 'aki-d209a2d3'},
|
52
|
-
'x86_64' => {:aki => 'aki-d409a2d5'}
|
53
|
-
}
|
54
|
-
|
55
|
-
},
|
56
|
-
|
57
|
-
'us-west-1' => {
|
58
|
-
:endpoint => 'ec2.us-west-1.amazonaws.com',
|
59
|
-
:location => 'us-west-1',
|
60
|
-
:kernel => {
|
61
|
-
'i386' => {:aki => 'aki-99a0f1dc'},
|
62
|
-
'x86_64' => {:aki => 'aki-9ba0f1de'}
|
63
|
-
}
|
64
|
-
},
|
65
|
-
|
66
|
-
'us-east-1' => {
|
67
|
-
:endpoint => 'ec2.amazonaws.com',
|
68
|
-
:location => '',
|
69
|
-
:kernel => {
|
70
|
-
'i386' => {:aki => 'aki-407d9529'},
|
71
|
-
'x86_64' => {:aki => 'aki-427d952b'}
|
72
|
-
}
|
73
|
-
}
|
74
|
-
}
|
75
29
|
|
76
30
|
ROOT_DEVICE_NAME = '/dev/sda1'
|
77
31
|
POLL_FREQ = 1 #second
|
@@ -79,24 +33,38 @@ module BoxGrinder
|
|
79
33
|
EC2_HOSTNAME_LOOKUP_TIMEOUT = 10
|
80
34
|
|
81
35
|
def validate
|
82
|
-
|
36
|
+
@ec2_endpoints = EC2Helper::endpoints
|
83
37
|
|
84
|
-
|
38
|
+
raise PluginValidationError, "You are trying to run this plugin on an invalid platform. You can run the EBS delivery plugin only on EC2." unless valid_platform?
|
39
|
+
|
40
|
+
@current_availability_zone = EC2Helper::current_availability_zone
|
41
|
+
@current_instance_id = EC2Helper::current_instance_id
|
42
|
+
@current_region = EC2Helper::availability_zone_to_region(@current_availability_zone)
|
85
43
|
|
86
44
|
set_default_config_value('availability_zone', @current_availability_zone)
|
87
45
|
set_default_config_value('delete_on_termination', true)
|
88
46
|
set_default_config_value('overwrite', false)
|
89
47
|
set_default_config_value('snapshot', false)
|
90
48
|
set_default_config_value('preserve_snapshots', false)
|
49
|
+
set_default_config_value('terminate_instances', false)
|
91
50
|
validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin')
|
92
51
|
|
93
52
|
raise PluginValidationError, "You can only convert to EBS type AMI appliances converted to EC2 format. Use '-p ec2' switch. For more info about EC2 plugin see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EC2_Platform_Plugin." unless @previous_plugin_info[:name] == :ec2
|
94
53
|
raise PluginValidationError, "You selected #{@plugin_config['availability_zone']} availability zone, but your instance is running in #{@current_availability_zone} zone. Please change availability zone in plugin configuration file to #{@current_availability_zone} (see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin) or use another instance in #{@plugin_config['availability_zone']} zone to create your EBS AMI." if @plugin_config['availability_zone'] != @current_availability_zone
|
54
|
+
|
55
|
+
@plugin_config['account_number'].to_s.gsub!(/-/, '')
|
56
|
+
|
57
|
+
AWS.config(:access_key_id => @plugin_config['access_key'],
|
58
|
+
:secret_access_key => @plugin_config['secret_access_key'],
|
59
|
+
:ec2_endpoint => @ec2_endpoints[@current_region][:endpoint],
|
60
|
+
:max_retries => 5,
|
61
|
+
:use_ssl => @plugin_config['use_ssl'])
|
62
|
+
|
63
|
+
@ec2 = AWS::EC2.new
|
64
|
+
@ec2helper = EC2Helper.new(@ec2, :log => @log)
|
95
65
|
end
|
96
66
|
|
97
67
|
def after_init
|
98
|
-
@region = availability_zone_to_region(@current_availability_zone)
|
99
|
-
|
100
68
|
register_supported_os('fedora', ['13', '14', '15'])
|
101
69
|
register_supported_os('rhel', ['6'])
|
102
70
|
register_supported_os('centos', ['5'])
|
@@ -105,63 +73,50 @@ module BoxGrinder
|
|
105
73
|
def execute
|
106
74
|
ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture"
|
107
75
|
|
108
|
-
@ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'],
|
109
|
-
:secret_access_key => @plugin_config['secret_access_key'],
|
110
|
-
:server => KERNELS[@region][:endpoint]
|
111
|
-
)
|
112
|
-
|
113
76
|
@log.debug "Checking if appliance is already registered..."
|
77
|
+
ami = @ec2helper.ami_by_name(ebs_appliance_name)
|
114
78
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
elsif ami_info
|
121
|
-
@log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_info.imageId}' (region: #{@region})."
|
79
|
+
if ami and @plugin_config['overwrite']
|
80
|
+
@log.info "Overwrite is enabled. Stomping existing assets."
|
81
|
+
stomp_ebs(ami)
|
82
|
+
elsif ami
|
83
|
+
@log.warn "EBS AMI '#{ami.name}' is already registered as '#{ami.id}' (region: #{@current_region})."
|
122
84
|
return
|
123
85
|
end
|
124
86
|
|
125
87
|
@log.info "Creating new EBS volume..."
|
126
|
-
|
127
88
|
size = 0
|
128
|
-
|
129
89
|
@appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] }
|
130
90
|
|
131
|
-
# create_volume, ceiling to avoid
|
132
|
-
|
133
|
-
|
134
|
-
begin
|
91
|
+
# create_volume, ceiling to avoid non-Integer values as per https://issues.jboss.org/browse/BGBUILD-224
|
92
|
+
volume = @ec2.volumes.create(:size => size.ceil.to_i, :availability_zone => @plugin_config['availability_zone'])
|
135
93
|
|
136
|
-
@log.debug "Volume #{
|
137
|
-
@log.debug "Waiting for EBS volume #{
|
94
|
+
@log.debug "Volume #{volume.id} created."
|
95
|
+
@log.debug "Waiting for EBS volume #{volume.id} to be available..."
|
138
96
|
|
139
97
|
# wait for volume to be created
|
140
|
-
wait_for_volume_status(
|
98
|
+
@ec2helper.wait_for_volume_status(:available, volume)
|
141
99
|
|
142
100
|
# get first free device to mount the volume
|
143
101
|
suffix = free_device_suffix
|
144
|
-
|
102
|
+
device_name = "/dev/sd#{suffix}"
|
145
103
|
@log.trace "Got free device suffix: '#{suffix}'"
|
146
|
-
@log.trace "Reading current instance id..."
|
147
104
|
|
148
|
-
|
149
|
-
|
105
|
+
@log.trace "Reading current instance id..."
|
106
|
+
# get_current_instance
|
107
|
+
current_instance = @ec2.instances[@current_instance_id]
|
150
108
|
|
151
|
-
@log.trace "Got: #{
|
109
|
+
@log.trace "Got: #{current_instance.id}"
|
152
110
|
@log.info "Attaching created volume..."
|
153
|
-
|
154
111
|
# attach the volume to current host
|
155
|
-
|
112
|
+
volume.attach_to(current_instance, device_name)
|
156
113
|
|
157
114
|
@log.debug "Waiting for EBS volume to be attached..."
|
158
|
-
|
159
115
|
# wait for volume to be attached
|
160
|
-
wait_for_volume_status(
|
116
|
+
@ec2helper.wait_for_volume_status(:in_use, volume)
|
161
117
|
|
162
118
|
@log.debug "Waiting for the attached EBS volume to be discovered by the OS"
|
163
|
-
|
164
|
-
wait_for_volume_attachment(suffix) # add rescue block for timeout when no suffix can be found then re-raise
|
119
|
+
wait_for_volume_attachment(suffix)
|
165
120
|
|
166
121
|
@log.info "Copying data to EBS volume..."
|
167
122
|
|
@@ -173,167 +128,82 @@ module BoxGrinder
|
|
173
128
|
end
|
174
129
|
|
175
130
|
@log.debug "Detaching EBS volume..."
|
176
|
-
|
177
|
-
@ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id)
|
131
|
+
volume.attachments.map(&:delete)
|
178
132
|
|
179
133
|
@log.debug "Waiting for EBS volume to become available..."
|
180
|
-
|
181
|
-
wait_for_volume_status('available', volume_id)
|
134
|
+
@ec2helper.wait_for_volume_status(:available, volume)
|
182
135
|
|
183
136
|
@log.info "Creating snapshot from EBS volume..."
|
137
|
+
snapshot = @ec2.snapshots.create(
|
138
|
+
:volume => volume,
|
139
|
+
:description => ebs_appliance_description)
|
184
140
|
|
185
|
-
|
186
|
-
|
187
|
-
:description => ebs_appliance_description)['snapshotId']
|
188
|
-
|
189
|
-
@log.debug "Waiting for snapshot #{snapshot_id} to be completed..."
|
190
|
-
|
191
|
-
wait_for_snapshot_status('completed', snapshot_id)
|
141
|
+
@log.debug "Waiting for snapshot #{snapshot.id} to be completed..."
|
142
|
+
@ec2helper.wait_for_snapshot_status(:completed, snapshot)
|
192
143
|
|
193
144
|
@log.debug "Deleting temporary EBS volume..."
|
194
|
-
|
195
|
-
@ec2.delete_volume(:volume_id => volume_id)
|
145
|
+
volume.delete
|
196
146
|
|
197
147
|
@log.info "Registering image..."
|
198
|
-
|
199
|
-
|
200
|
-
:block_device_mapping => [{
|
201
|
-
:device_name => '/dev/sda1',
|
202
|
-
:ebs_snapshot_id => snapshot_id,
|
203
|
-
:ebs_delete_on_termination => @plugin_config['delete_on_termination']
|
204
|
-
},
|
205
|
-
{
|
206
|
-
:device_name => '/dev/sdb',
|
207
|
-
:virtual_name => 'ephemeral0'
|
208
|
-
},
|
209
|
-
{
|
210
|
-
:device_name => '/dev/sdc',
|
211
|
-
:virtual_name => 'ephemeral1'
|
212
|
-
},
|
213
|
-
{
|
214
|
-
:device_name => '/dev/sdd',
|
215
|
-
:virtual_name => 'ephemeral2'
|
216
|
-
},
|
217
|
-
{
|
218
|
-
:device_name => '/dev/sde',
|
219
|
-
:virtual_name => 'ephemeral3'
|
220
|
-
}],
|
148
|
+
image = @ec2.images.create(
|
149
|
+
:name => ebs_appliance_name,
|
221
150
|
:root_device_name => ROOT_DEVICE_NAME,
|
151
|
+
:block_device_mappings => { ROOT_DEVICE_NAME => {
|
152
|
+
:snapshot => snapshot,
|
153
|
+
:delete_on_termination => @plugin_config['delete_on_termination']
|
154
|
+
},
|
155
|
+
'/dev/sdb' => 'ephemeral0',
|
156
|
+
'/dev/sdc' => 'ephemeral1',
|
157
|
+
'/dev/sdd' => 'ephemeral2',
|
158
|
+
'/dev/sde' => 'ephemeral3'},
|
222
159
|
:architecture => @appliance_config.hardware.base_arch,
|
223
|
-
:kernel_id =>
|
224
|
-
:
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
@log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})"
|
160
|
+
:kernel_id => @ec2_endpoints[@current_region][:kernel][@appliance_config.hardware.base_arch.intern][:aki],
|
161
|
+
:description => ebs_appliance_description)
|
162
|
+
|
163
|
+
@log.info "Waiting for the new EBS AMI to become available"
|
164
|
+
@ec2helper.wait_for_image_state(:available, image)
|
165
|
+
@log.info "EBS AMI '#{image.name}' registered: #{image.id} (region: #{@current_region})"
|
166
|
+
rescue Timeout::Error
|
167
|
+
@log.error "An operation timed out. Manual intervention may be necessary to complete the task."
|
168
|
+
raise
|
233
169
|
end
|
234
170
|
|
235
|
-
def
|
236
|
-
|
237
|
-
@ec2.describe_volumes(:volume_id => volume_id).volumeSet.item.each do |volume|
|
238
|
-
return volume if volume.volumeId == volume_id
|
239
|
-
end
|
240
|
-
rescue AWS::Error, AWS::InvalidVolumeIDNotFound => e# only InvalidVolumeIDNotFound should be returned when no volume found, but is not always doing so at present.
|
241
|
-
@log.trace "Error getting volume info: #{e}"
|
242
|
-
return nil
|
243
|
-
end
|
244
|
-
nil
|
171
|
+
def ami_by_name(name)
|
172
|
+
@ec2helper.ami_by_name(name, @plugin_config['account_number'])
|
245
173
|
end
|
246
174
|
|
247
|
-
|
248
|
-
begin
|
249
|
-
@ec2.describe_snapshots(:snapshot_id => snapshot_id).snapshotSet.item.each do |snapshot|
|
250
|
-
return snapshot if snapshot.snapshotId == snapshot_id
|
251
|
-
end
|
252
|
-
rescue AWS::InvalidSnapshotIDNotFound
|
253
|
-
return nil
|
254
|
-
end
|
255
|
-
nil
|
256
|
-
end
|
175
|
+
alias :already_registered? :ami_by_name
|
257
176
|
|
258
|
-
def
|
259
|
-
|
260
|
-
|
177
|
+
def terminate_instances(instances)
|
178
|
+
instances.map(&:terminate)
|
179
|
+
instances.each do |i|
|
180
|
+
@ec2helper.wait_for_instance_death(i)
|
261
181
|
end
|
262
|
-
nil
|
263
182
|
end
|
264
183
|
|
265
|
-
def
|
266
|
-
#
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
184
|
+
def stomp_ebs(ami)
|
185
|
+
#Find any instances that are running, if they are not stopped then abort.
|
186
|
+
if live = @ec2helper.live_instances(ami)
|
187
|
+
if @plugin_config['terminate_instances']
|
188
|
+
@log.info "Terminating the following instances: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
|
189
|
+
terminate_instances(live)
|
190
|
+
else
|
191
|
+
raise "There are still instances of #{ami.id} running, you should terminate them after " <<
|
192
|
+
"preserving any important data: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
|
272
193
|
end
|
273
194
|
end
|
274
|
-
return instances.uniq unless instances.empty?
|
275
|
-
nil
|
276
|
-
end
|
277
|
-
|
278
|
-
def stomp_ebs(ami_info)
|
279
|
-
|
280
|
-
device = block_device_from_ami(ami_info, ROOT_DEVICE_NAME)
|
281
195
|
|
282
|
-
|
283
|
-
|
284
|
-
volume_id = snapshot_info.volumeId
|
285
|
-
volume_info = get_volume_info(volume_id)
|
286
|
-
|
287
|
-
@log.trace "Volume info for #{volume_id} : #{PP::pp(volume_info,"")}"
|
288
|
-
@log.info "Finding any existing image with the block store attached"
|
289
|
-
|
290
|
-
if instances = get_instances(ami_info.imageId)
|
291
|
-
raise "There are still instances of #{ami_info.imageId} running, you must stop them: #{instances.collect {|i| i.instanceId}.join(",")}"
|
292
|
-
end
|
293
|
-
|
294
|
-
if volume_info #if the physical volume exists
|
295
|
-
unless volume_info.status == 'available'
|
296
|
-
begin
|
297
|
-
@log.info "Forcibly detaching block store #{volume_info.volumeId}"
|
298
|
-
@ec2.detach_volume(:volume_id => volume_info.volumeId, :force => true)
|
299
|
-
rescue AWS::IncorrectState
|
300
|
-
@log.debug "State of the volume has changed, our data must have been stale. This should not be fatal."
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
@log.debug "Waiting for volume to become detached"
|
305
|
-
wait_for_volume_status('available', volume_info.volumeId)
|
306
|
-
|
307
|
-
begin
|
308
|
-
@log.info "Deleting block store"
|
309
|
-
@ec2.delete_volume(:volume_id => volume_info.volumeId)
|
310
|
-
@log.debug "Waiting for volume deletion to be confirmed"
|
311
|
-
wait_for_volume_status('deleted', volume_info.volumeId)
|
312
|
-
rescue AWS::InvalidVolumeIDNotFound
|
313
|
-
@log.debug "An external entity has probably deleted the volume just before we tried to. This should not be fatal."
|
314
|
-
end
|
315
|
-
end
|
196
|
+
@log.info("Finding the primary snapshot associated with #{ami.id}.")
|
197
|
+
primary_snapshot = @ec2helper.snapshot_by_id(ami.block_device_mappings[ami.root_device_name].snapshot_id)
|
316
198
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
rescue AWS::InvalidAMIIDUnavailable, AWS::InvalidAMIIDNotFound
|
321
|
-
@log.debug "An external entity has already deregistered the AMI just before we tried to. This should not be fatal."
|
322
|
-
end
|
199
|
+
@log.info("De-registering the EBS AMI.")
|
200
|
+
ami.deregister
|
201
|
+
@ec2helper.wait_for_image_death(ami)
|
323
202
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
@ec2.delete_snapshot(:snapshot_id => snapshot_info.snapshotId)
|
328
|
-
rescue AWS::InvalidSnapshotIDNotFound
|
329
|
-
@log.debug "An external entity has probably deleted the snapshot just before we tried to. This should not be fatal."
|
330
|
-
end
|
331
|
-
end
|
332
|
-
else
|
333
|
-
@log.error "Expected device #{ROOT_DEVICE_NAME} was not found on the image."
|
334
|
-
return false
|
203
|
+
if !@plugin_config['preserve_snapshots'] and primary_snapshot
|
204
|
+
@log.info("Deleting the primary snapshot.")
|
205
|
+
primary_snapshot.delete
|
335
206
|
end
|
336
|
-
true
|
337
207
|
end
|
338
208
|
|
339
209
|
def ebs_appliance_name
|
@@ -343,7 +213,7 @@ module BoxGrinder
|
|
343
213
|
|
344
214
|
snapshot = 1
|
345
215
|
|
346
|
-
while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
|
216
|
+
while @ec2helper.already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
|
347
217
|
snapshot += 1
|
348
218
|
end
|
349
219
|
# Reuse the last key (if there was one)
|
@@ -352,80 +222,19 @@ module BoxGrinder
|
|
352
222
|
"#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}"
|
353
223
|
end
|
354
224
|
|
355
|
-
def ami_info(name)
|
356
|
-
images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/,''))
|
357
|
-
return false if images.nil?
|
358
|
-
images = images.imagesSet
|
359
|
-
|
360
|
-
for image in images.item do
|
361
|
-
return image if image.name == name
|
362
|
-
end
|
363
|
-
false
|
364
|
-
end
|
365
|
-
|
366
|
-
def already_registered?(name)
|
367
|
-
info = ami_info(name)
|
368
|
-
return info.imageId if info
|
369
|
-
false
|
370
|
-
end
|
371
|
-
|
372
225
|
def adjust_fstab(guestfs)
|
373
226
|
guestfs.sh("cat /etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > /etc/fstab.new")
|
374
227
|
guestfs.mv("/etc/fstab.new", "/etc/fstab")
|
375
228
|
end
|
376
229
|
|
377
|
-
def wait_with_timeout(cycle_seconds, timeout_seconds)
|
378
|
-
Timeout::timeout(timeout_seconds) do
|
379
|
-
while not yield
|
380
|
-
sleep cycle_seconds
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
230
|
def wait_for_volume_attachment(suffix)
|
386
|
-
wait_with_timeout(POLL_FREQ, TIMEOUT){ device_for_suffix(suffix) != nil }
|
387
|
-
end
|
388
|
-
|
389
|
-
def wait_for_snapshot_status(status, snapshot_id)
|
390
|
-
begin
|
391
|
-
progress = -1
|
392
|
-
snapshot = nil
|
393
|
-
wait_with_timeout(POLL_FREQ, TIMEOUT) do
|
394
|
-
snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first
|
395
|
-
current_progress = snapshot.progress.to_i
|
396
|
-
unless progress == current_progress
|
397
|
-
@log.info "Progress: #{current_progress}%"
|
398
|
-
progress = current_progress
|
399
|
-
end
|
400
|
-
snapshot['status'] == status
|
401
|
-
end
|
402
|
-
rescue Exception
|
403
|
-
snapshot.ownerId='<REDACTED>' #potentially sensitive?
|
404
|
-
@log.debug "Polling of snapshot #{snapshot_id} for status '#{status}' failed: " <<
|
405
|
-
"#{PP::pp(snapshot)}" unless snapshot.nil?
|
406
|
-
raise
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
def wait_for_volume_status(status, volume_id)
|
411
|
-
begin
|
412
|
-
volume=nil
|
413
|
-
wait_with_timeout(POLL_FREQ, TIMEOUT) do
|
414
|
-
volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first
|
415
|
-
volume['status'] == status
|
416
|
-
end
|
417
|
-
rescue Exception
|
418
|
-
@log.debug "Polling of volume #{volume_id} for status '#{status}' failed: " <<
|
419
|
-
"#{PP::pp(volume)}" unless volume.nil?
|
420
|
-
raise
|
421
|
-
end
|
231
|
+
@ec2helper.wait_with_timeout(POLL_FREQ, TIMEOUT){ device_for_suffix(suffix) != nil }
|
422
232
|
end
|
423
233
|
|
424
234
|
def device_for_suffix(suffix)
|
425
235
|
return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}")
|
426
236
|
return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}")
|
427
237
|
nil
|
428
|
-
#raise "Device for suffix '#{suffix}' not found!"
|
429
238
|
end
|
430
239
|
|
431
240
|
def free_device_suffix
|
@@ -437,9 +246,9 @@ module BoxGrinder
|
|
437
246
|
|
438
247
|
def valid_platform?
|
439
248
|
begin
|
440
|
-
region = availability_zone_to_region(
|
441
|
-
return true if
|
442
|
-
@log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{
|
249
|
+
region = EC2Helper::availability_zone_to_region(EC2Helper::current_availability_zone)
|
250
|
+
return true if @ec2_endpoints.has_key? region
|
251
|
+
@log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{@ec2_endpoints.join(", ")}"
|
443
252
|
rescue Net::HTTPServerException => e
|
444
253
|
@log.warn "An error was returned when attempting to retrieve the ec2 hostname: #{e.to_s}"
|
445
254
|
rescue Timeout::Error => t
|
@@ -448,19 +257,6 @@ module BoxGrinder
|
|
448
257
|
false
|
449
258
|
end
|
450
259
|
|
451
|
-
def get_ec2_availability_zone
|
452
|
-
timeout(EC2_HOSTNAME_LOOKUP_TIMEOUT) do
|
453
|
-
req = Net::HTTP::Get.new('/latest/meta-data/placement/availability-zone/')
|
454
|
-
res = Net::HTTP.start('169.254.169.254', 80) {|http| http.request(req)}
|
455
|
-
return res.body if Net::HTTPSuccess
|
456
|
-
res.error!
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
def availability_zone_to_region(availability_zone)
|
461
|
-
availability_zone.scan(/((\w+)-(\w+)-(\d+))/).flatten.first
|
462
|
-
end
|
463
|
-
|
464
260
|
end
|
465
261
|
end
|
466
262
|
|