boxgrinder-build 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +14 -0
- data/README.md +136 -0
- data/Rakefile +11 -6
- data/bin/boxgrinder-build +199 -0
- data/boxgrinder-build.gemspec +26 -14
- data/lib/boxgrinder-build/appliance.rb +6 -6
- data/lib/boxgrinder-build/helpers/guestfs-helper.rb +5 -3
- data/lib/boxgrinder-build/helpers/image-helper.rb +6 -0
- data/lib/boxgrinder-build/helpers/plugin-helper.rb +15 -4
- data/lib/boxgrinder-build/plugins/base-plugin.rb +1 -0
- data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +280 -0
- data/lib/boxgrinder-build/plugins/delivery/local/local-plugin.rb +66 -0
- data/lib/boxgrinder-build/plugins/delivery/s3/s3-plugin.rb +242 -0
- data/lib/boxgrinder-build/plugins/delivery/s3/src/cert-ec2.pem +23 -0
- data/lib/boxgrinder-build/plugins/delivery/sftp/sftp-plugin.rb +152 -0
- data/lib/boxgrinder-build/plugins/delivery/usb/usb-plugin.rb +43 -0
- data/lib/boxgrinder-build/plugins/os/centos/centos-plugin.rb +46 -0
- data/lib/boxgrinder-build/plugins/os/fedora/fedora-plugin.rb +61 -0
- data/lib/boxgrinder-build/plugins/os/rhel/rhel-plugin.rb +67 -0
- data/lib/boxgrinder-build/plugins/os/rpm-based/kickstart.rb +118 -0
- data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +202 -0
- data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-dependency-validator.rb +153 -0
- data/lib/boxgrinder-build/plugins/os/rpm-based/src/appliance.ks.erb +37 -0
- data/lib/boxgrinder-build/plugins/os/rpm-based/src/base.repo +4 -0
- data/lib/boxgrinder-build/plugins/os/rpm-based/src/motd.init +21 -0
- data/lib/boxgrinder-build/plugins/platform/ec2/ec2-plugin.rb +239 -0
- data/lib/boxgrinder-build/plugins/platform/ec2/src/fstab_32bit +7 -0
- data/lib/boxgrinder-build/plugins/platform/ec2/src/fstab_64bit +7 -0
- data/lib/boxgrinder-build/plugins/platform/ec2/src/ifcfg-eth0 +7 -0
- data/lib/boxgrinder-build/plugins/platform/ec2/src/menu.lst +6 -0
- data/lib/boxgrinder-build/plugins/platform/ec2/src/rc_local +19 -0
- data/lib/boxgrinder-build/plugins/platform/virtualbox/virtualbox-plugin.rb +62 -0
- data/lib/boxgrinder-build/plugins/platform/vmware/src/README-enterprise +18 -0
- data/lib/boxgrinder-build/plugins/platform/vmware/src/README-personal +16 -0
- data/lib/boxgrinder-build/plugins/platform/vmware/src/base.vmdk +20 -0
- data/lib/boxgrinder-build/plugins/platform/vmware/src/base.vmx +45 -0
- data/lib/boxgrinder-build/plugins/platform/vmware/vmware-plugin.rb +194 -0
- data/rubygem-boxgrinder-build.spec +73 -10
- data/spec/appliance-spec.rb +2 -2
- data/spec/helpers/guestfs-helper-spec.rb +4 -2
- data/spec/helpers/image-helper-spec.rb +4 -0
- data/spec/helpers/plugin-helper-spec.rb +0 -38
- data/spec/managers/plugin-manager-spec.rb +6 -6
- data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +230 -0
- data/spec/plugins/delivery/ebs/ebs.yaml +3 -0
- data/spec/plugins/delivery/local/local-plugin-spec.rb +133 -0
- data/spec/plugins/delivery/s3/s3-plugin-spec.rb +351 -0
- data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +26 -0
- data/spec/plugins/os/centos/centos-plugin-spec.rb +52 -0
- data/spec/plugins/os/fedora/fedora-plugin-spec.rb +73 -0
- data/spec/plugins/os/rhel/rhel-plugin-spec.rb +158 -0
- data/spec/plugins/os/rpm-based/kickstart-spec.rb +129 -0
- data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +162 -0
- data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +50 -0
- data/spec/plugins/os/rpm-based/src/jeos-f13-plain.ks +20 -0
- data/spec/plugins/os/rpm-based/src/jeos-f13-without-version.ks +22 -0
- data/spec/plugins/os/rpm-based/src/jeos-f13.ks +23 -0
- data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +339 -0
- data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +118 -0
- data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +299 -0
- metadata +149 -25
- data/README +0 -7
- data/bin/boxgrinder +0 -128
- data/lib/boxgrinder-build/helpers/thor-helper.rb +0 -85
@@ -0,0 +1,280 @@
|
|
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 'rubygems'
|
20
|
+
require 'boxgrinder-build/plugins/base-plugin'
|
21
|
+
require 'AWS'
|
22
|
+
require 'open-uri'
|
23
|
+
|
24
|
+
module BoxGrinder
|
25
|
+
class EBSPlugin < BasePlugin
|
26
|
+
KERNELS = {
|
27
|
+
'eu-west-1' => {
|
28
|
+
'i386' => {:aki => 'aki-4deec439'},
|
29
|
+
'x86_64' => {:aki => 'aki-4feec43b'}
|
30
|
+
},
|
31
|
+
'ap-southeast-1' => {
|
32
|
+
'i386' => {:aki => 'aki-13d5aa41'},
|
33
|
+
'x86_64' => {:aki => 'aki-11d5aa43'}
|
34
|
+
},
|
35
|
+
'us-west-1' => {
|
36
|
+
'i386' => {:aki => 'aki-99a0f1dc'},
|
37
|
+
'x86_64' => {:aki => 'aki-9ba0f1de'}
|
38
|
+
},
|
39
|
+
'us-east-1' => {
|
40
|
+
'i386' => {:aki => 'aki-407d9529'},
|
41
|
+
'x86_64' => {:aki => 'aki-427d952b'}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
def after_init
|
46
|
+
begin
|
47
|
+
@current_avaibility_zone = open('http://169.254.169.254/latest/meta-data/placement/availability-zone').string
|
48
|
+
@region = @current_avaibility_zone.scan(/((\w+)-(\w+)-(\d+))/).flatten.first
|
49
|
+
rescue
|
50
|
+
@current_avaibility_zone = nil
|
51
|
+
@region = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
set_default_config_value('availability_zone', @current_avaibility_zone)
|
55
|
+
set_default_config_value('delete_on_termination', true)
|
56
|
+
|
57
|
+
register_supported_os('fedora', ['13', '14', '15'])
|
58
|
+
register_supported_os('rhel', ['6'])
|
59
|
+
end
|
60
|
+
|
61
|
+
def execute(type = :ebs)
|
62
|
+
validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin')
|
63
|
+
|
64
|
+
raise "You try to run this plugin on invalid platform. You can run EBS delivery plugin only on EC2." unless valid_platform?
|
65
|
+
raise "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
|
66
|
+
raise "You selected #{@plugin_config['availability_zone']} avaibility zone, but your instance is running in #{@current_avaibility_zone} zone. Please change avaibility zone in plugin configuration file to #{@current_avaibility_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_avaibility_zone
|
67
|
+
|
68
|
+
ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture"
|
69
|
+
|
70
|
+
@ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'], :secret_access_key => @plugin_config['secret_access_key'])
|
71
|
+
|
72
|
+
@log.debug "Checking if appliance is already registered..."
|
73
|
+
|
74
|
+
ami_id = already_registered?(ebs_appliance_name)
|
75
|
+
|
76
|
+
if ami_id
|
77
|
+
@log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_id}' (region: #{@region})."
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
@log.info "Creating new EBS volume..."
|
82
|
+
|
83
|
+
size = 0
|
84
|
+
|
85
|
+
@appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] }
|
86
|
+
|
87
|
+
# create_volume with 10GB size
|
88
|
+
volume_id = @ec2.create_volume(:size => size.to_s, :availability_zone => @plugin_config['availability_zone'])['volumeId']
|
89
|
+
|
90
|
+
@log.debug "Volume #{volume_id} created."
|
91
|
+
@log.debug "Waiting for EBS volume #{volume_id} to be available..."
|
92
|
+
|
93
|
+
# wait fo volume to be created
|
94
|
+
wait_for_volume_status('available', volume_id)
|
95
|
+
|
96
|
+
# get first free device to mount the volume
|
97
|
+
suffix = free_device_suffix
|
98
|
+
|
99
|
+
@log.trace "Got free device suffix: '#{suffix}'"
|
100
|
+
@log.trace "Reading current instance id..."
|
101
|
+
|
102
|
+
# read current instance id
|
103
|
+
instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').string
|
104
|
+
|
105
|
+
@log.trace "Got: #{instance_id}"
|
106
|
+
@log.info "Attaching created volume..."
|
107
|
+
|
108
|
+
# attach the volume to current host
|
109
|
+
@ec2.attach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id)
|
110
|
+
|
111
|
+
@log.debug "Waiting for EBS volume to be attached..."
|
112
|
+
|
113
|
+
# wait for volume to be attached
|
114
|
+
wait_for_volume_status('in-use', volume_id)
|
115
|
+
|
116
|
+
sleep 5 # let's wait to discover the attached volume by OS
|
117
|
+
|
118
|
+
@log.info "Copying data to EBS volume..."
|
119
|
+
|
120
|
+
ec2_disk_mount_dir = "#{@dir.tmp}/ec2-#{rand(9999999999).to_s.center(10, rand(9).to_s)}"
|
121
|
+
ebs_disk_mount_dir = "#{@dir.tmp}/ebs-#{rand(9999999999).to_s.center(10, rand(9).to_s)}"
|
122
|
+
|
123
|
+
FileUtils.mkdir_p(ec2_disk_mount_dir)
|
124
|
+
FileUtils.mkdir_p(ebs_disk_mount_dir)
|
125
|
+
|
126
|
+
begin
|
127
|
+
ec2_mounts = @image_helper.mount_image(@previous_deliverables.disk, ec2_disk_mount_dir)
|
128
|
+
rescue => e
|
129
|
+
@log.debug e
|
130
|
+
raise "Error while mounting image. See logs for more info"
|
131
|
+
end
|
132
|
+
|
133
|
+
@log.debug "Creating filesystem on volume..."
|
134
|
+
|
135
|
+
@image_helper.create_filesystem(device_for_suffix(suffix))
|
136
|
+
@exec_helper.execute("mount #{device_for_suffix(suffix)} #{ebs_disk_mount_dir}")
|
137
|
+
|
138
|
+
@log.debug "Syncing files..."
|
139
|
+
|
140
|
+
@image_helper.sync_files(ec2_disk_mount_dir, ebs_disk_mount_dir)
|
141
|
+
|
142
|
+
@log.debug "Adjusting /etc/fstab..."
|
143
|
+
|
144
|
+
adjust_fstab(ebs_disk_mount_dir)
|
145
|
+
|
146
|
+
@exec_helper.execute("umount #{ebs_disk_mount_dir}")
|
147
|
+
@image_helper.umount_image(@previous_deliverables.disk, ec2_disk_mount_dir, ec2_mounts)
|
148
|
+
|
149
|
+
FileUtils.rm_rf(ebs_disk_mount_dir)
|
150
|
+
FileUtils.rm_rf(ec2_disk_mount_dir)
|
151
|
+
|
152
|
+
@log.debug "Detaching EBS volume..."
|
153
|
+
|
154
|
+
@ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id)
|
155
|
+
|
156
|
+
@log.debug "Waiting for EBS volume to be available..."
|
157
|
+
|
158
|
+
wait_for_volume_status('available', volume_id)
|
159
|
+
|
160
|
+
@log.info "Creating snapshot from EBS volume..."
|
161
|
+
|
162
|
+
snapshot_id = @ec2.create_snapshot(
|
163
|
+
:volume_id => volume_id,
|
164
|
+
:description => ebs_appliance_description)['snapshotId']
|
165
|
+
|
166
|
+
@log.debug "Waiting for snapshot #{snapshot_id} to be completed..."
|
167
|
+
|
168
|
+
wait_for_snapshot_status('completed', snapshot_id)
|
169
|
+
|
170
|
+
@log.debug "Deleting temporary EBS volume..."
|
171
|
+
|
172
|
+
@ec2.delete_volume(:volume_id => volume_id)
|
173
|
+
|
174
|
+
@log.info "Registering image..."
|
175
|
+
|
176
|
+
image_id = @ec2.register_image(
|
177
|
+
:block_device_mapping => [{
|
178
|
+
:device_name => '/dev/sda1',
|
179
|
+
:ebs_snapshot_id => snapshot_id,
|
180
|
+
:ebs_delete_on_termination => @plugin_config['delete_on_termination']
|
181
|
+
},
|
182
|
+
{
|
183
|
+
:device_name => '/dev/sdb',
|
184
|
+
:virtual_name => 'ephemeral0'
|
185
|
+
},
|
186
|
+
{
|
187
|
+
:device_name => '/dev/sdc',
|
188
|
+
:virtual_name => 'ephemeral1'
|
189
|
+
},
|
190
|
+
{
|
191
|
+
:device_name => '/dev/sdd',
|
192
|
+
:virtual_name => 'ephemeral2'
|
193
|
+
},
|
194
|
+
{
|
195
|
+
:device_name => '/dev/sde',
|
196
|
+
:virtual_name => 'ephemeral3'
|
197
|
+
}],
|
198
|
+
:root_device_name => '/dev/sda1',
|
199
|
+
:architecture => @appliance_config.hardware.base_arch,
|
200
|
+
:kernel_id => KERNELS[@region][@appliance_config.hardware.base_arch][:aki],
|
201
|
+
:name => ebs_appliance_name,
|
202
|
+
:description => ebs_appliance_description)['imageId']
|
203
|
+
|
204
|
+
@log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})"
|
205
|
+
end
|
206
|
+
|
207
|
+
def ebs_appliance_name
|
208
|
+
base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}"
|
209
|
+
|
210
|
+
return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot']
|
211
|
+
|
212
|
+
snapshot = 1
|
213
|
+
|
214
|
+
while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
|
215
|
+
snapshot += 1
|
216
|
+
end
|
217
|
+
|
218
|
+
"#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}"
|
219
|
+
end
|
220
|
+
|
221
|
+
def already_registered?(name)
|
222
|
+
images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/, ''))
|
223
|
+
|
224
|
+
return false if images.nil? or images['imagesSet'].nil?
|
225
|
+
|
226
|
+
images['imagesSet']['item'].each { |image| return image['imageId'] if image['name'] == name }
|
227
|
+
|
228
|
+
false
|
229
|
+
end
|
230
|
+
|
231
|
+
def adjust_fstab(ebs_mount_dir)
|
232
|
+
@exec_helper.execute("cat #{ebs_mount_dir}/etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > #{ebs_mount_dir}/etc/fstab.new")
|
233
|
+
@exec_helper.execute("mv #{ebs_mount_dir}/etc/fstab.new #{ebs_mount_dir}/etc/fstab")
|
234
|
+
end
|
235
|
+
|
236
|
+
def wait_for_snapshot_status(status, snapshot_id)
|
237
|
+
snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first
|
238
|
+
|
239
|
+
unless snapshot['status'] == status
|
240
|
+
sleep 2
|
241
|
+
wait_for_snapshot_status(status, snapshot_id)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def wait_for_volume_status(status, volume_id)
|
246
|
+
volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first
|
247
|
+
|
248
|
+
unless volume['status'] == status
|
249
|
+
sleep 2
|
250
|
+
wait_for_volume_status(status, volume_id)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def device_for_suffix(suffix)
|
255
|
+
return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}")
|
256
|
+
return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}")
|
257
|
+
|
258
|
+
raise "Not found device for suffix #{suffix}"
|
259
|
+
end
|
260
|
+
|
261
|
+
def free_device_suffix
|
262
|
+
("f".."p").each do |suffix|
|
263
|
+
return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}")
|
264
|
+
end
|
265
|
+
|
266
|
+
raise "Found too many attached devices. Cannot attach EBS volume."
|
267
|
+
end
|
268
|
+
|
269
|
+
def valid_platform?
|
270
|
+
begin
|
271
|
+
open("http://169.254.169.254/1.0/meta-data/local-ipv4")
|
272
|
+
true
|
273
|
+
rescue
|
274
|
+
false
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
plugin :class => BoxGrinder::EBSPlugin, :type => :delivery, :name => :ebs, :full_name => "Elastic Block Storage"
|
@@ -0,0 +1,66 @@
|
|
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 'rubygems'
|
20
|
+
require 'boxgrinder-build/helpers/package-helper'
|
21
|
+
require 'boxgrinder-build/plugins/base-plugin'
|
22
|
+
|
23
|
+
module BoxGrinder
|
24
|
+
class LocalPlugin < BasePlugin
|
25
|
+
def after_init
|
26
|
+
set_default_config_value('overwrite', false)
|
27
|
+
set_default_config_value('package', true)
|
28
|
+
|
29
|
+
if @plugin_config['package']
|
30
|
+
register_deliverable(:package => "#{@appliance_config.name}-#{@appliance_config.version}.#{@appliance_config.release}-#{@appliance_config.os.name}-#{@appliance_config.os.version}-#{@appliance_config.hardware.arch}-#{current_platform}.tgz")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute(type = :local)
|
35
|
+
validate_plugin_config(['path'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#Local_delivery_plugin')
|
36
|
+
|
37
|
+
if @plugin_config['overwrite'] or !deliverables_exists?
|
38
|
+
PackageHelper.new(@config, @appliance_config, :log => @log, :exec_helper => @exec_helper).package(File.dirname(@previous_deliverables[:disk]), @deliverables[:package]) if @plugin_config['package']
|
39
|
+
|
40
|
+
FileUtils.mkdir_p @plugin_config['path']
|
41
|
+
|
42
|
+
@log.debug "Copying files to '#{@plugin_config['path']}'..."
|
43
|
+
|
44
|
+
(@plugin_config['package'] ? @deliverables : @previous_deliverables).each_value do |file|
|
45
|
+
@log.debug "Copying '#{file}'..."
|
46
|
+
@exec_helper.execute("cp '#{file}' '#{@plugin_config['path']}'")
|
47
|
+
end
|
48
|
+
@log.info "Appliance delivered to '#{@plugin_config['path']}'."
|
49
|
+
else
|
50
|
+
@log.info "Appliance already delivered to '#{@plugin_config['path']}'."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def deliverables_exists?
|
55
|
+
(@plugin_config['package'] ? @deliverables : @previous_deliverables).each_value do |file|
|
56
|
+
return false unless File.exists?("#{@plugin_config['path']}/#{File.basename(file)}")
|
57
|
+
end
|
58
|
+
|
59
|
+
@move_deliverables = false
|
60
|
+
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
plugin :class => BoxGrinder::LocalPlugin, :type => :delivery, :name => :local, :full_name => "Local file system"
|
@@ -0,0 +1,242 @@
|
|
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 'rubygems'
|
20
|
+
require 'boxgrinder-build/plugins/base-plugin'
|
21
|
+
require 'boxgrinder-build/helpers/package-helper'
|
22
|
+
require 'AWS'
|
23
|
+
require 'aws'
|
24
|
+
|
25
|
+
# TODO remove this when it'll become not necessary
|
26
|
+
# quick fix for old active_support require issue in EPEL 5
|
27
|
+
require 'active_support/basic_object'
|
28
|
+
require 'active_support/duration'
|
29
|
+
|
30
|
+
module BoxGrinder
|
31
|
+
class S3Plugin < BasePlugin
|
32
|
+
REGION_OPTIONS = {
|
33
|
+
'eu-west-1' => {
|
34
|
+
:endpoint => 's3.amazonaws.com',
|
35
|
+
:location => 'EU',
|
36
|
+
:kernel => {
|
37
|
+
'i386' => {:aki => 'aki-4deec439'},
|
38
|
+
'x86_64' => {:aki => 'aki-4feec43b'}
|
39
|
+
}
|
40
|
+
},
|
41
|
+
|
42
|
+
'ap-southeast-1' => {
|
43
|
+
:endpoint => 's3-ap-southeast-1.amazonaws.com',
|
44
|
+
:location => 'ap-southeast-1',
|
45
|
+
:kernel => {
|
46
|
+
'i386' => {:aki => 'aki-13d5aa41'},
|
47
|
+
'x86_64' => {:aki => 'aki-11d5aa43'}
|
48
|
+
}
|
49
|
+
},
|
50
|
+
|
51
|
+
'us-west-1' => {
|
52
|
+
:endpoint => 's3-us-west-1.amazonaws.com',
|
53
|
+
:location => 'us-west-1',
|
54
|
+
:kernel => {
|
55
|
+
'i386' => {:aki => 'aki-99a0f1dc'},
|
56
|
+
'x86_64' => {:aki => 'aki-9ba0f1de'}
|
57
|
+
}
|
58
|
+
},
|
59
|
+
|
60
|
+
'us-east-1' => {
|
61
|
+
:endpoint => 's3.amazonaws.com',
|
62
|
+
:location => '',
|
63
|
+
:kernel => {
|
64
|
+
'i386' => {:aki => 'aki-407d9529'},
|
65
|
+
'x86_64' => {:aki => 'aki-427d952b'}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
def after_init
|
71
|
+
set_default_config_value('overwrite', false)
|
72
|
+
set_default_config_value('path', '/')
|
73
|
+
set_default_config_value('region', 'us-east-1')
|
74
|
+
|
75
|
+
register_supported_os("fedora", ['13', '14', '15'])
|
76
|
+
register_supported_os("centos", ['5'])
|
77
|
+
register_supported_os("rhel", ['5', '6'])
|
78
|
+
|
79
|
+
@ami_build_dir = "#{@dir.base}/ami"
|
80
|
+
@ami_manifest = "#{@ami_build_dir}/#{@appliance_config.name}.ec2.manifest.xml"
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute(type = :ami)
|
84
|
+
validate_plugin_config(['bucket', 'access_key', 'secret_access_key'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#S3_Delivery_Plugin')
|
85
|
+
|
86
|
+
case type
|
87
|
+
when :s3
|
88
|
+
upload_to_bucket(@previous_deliverables)
|
89
|
+
when :cloudfront
|
90
|
+
upload_to_bucket(@previous_deliverables, 'public-read')
|
91
|
+
when :ami
|
92
|
+
set_default_config_value('snapshot', false)
|
93
|
+
validate_plugin_config(['cert_file', 'key_file', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#S3_Delivery_Plugin')
|
94
|
+
|
95
|
+
@plugin_config['account_number'] = @plugin_config['account_number'].to_s.gsub(/-/, '')
|
96
|
+
|
97
|
+
@ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'], :secret_access_key => @plugin_config['secret_access_key'], :server => "ec2.#{@plugin_config['region']}.amazonaws.com")
|
98
|
+
|
99
|
+
ami_dir = ami_key(@appliance_config.name, @plugin_config['path'])
|
100
|
+
ami_manifest_key = "#{ami_dir}/#{@appliance_config.name}.ec2.manifest.xml"
|
101
|
+
|
102
|
+
if !s3_object_exists?(ami_manifest_key) or @plugin_config['snapshot']
|
103
|
+
bundle_image(@previous_deliverables)
|
104
|
+
fix_sha1_sum
|
105
|
+
upload_image(ami_dir)
|
106
|
+
else
|
107
|
+
@log.debug "AMI for #{@appliance_config.name} appliance already uploaded, skipping..."
|
108
|
+
end
|
109
|
+
|
110
|
+
register_image(ami_manifest_key)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# https://jira.jboss.org/browse/BGBUILD-34
|
115
|
+
def fix_sha1_sum
|
116
|
+
ami_manifest = File.open(@ami_manifest).read
|
117
|
+
ami_manifest.gsub!('(stdin)= ', '')
|
118
|
+
|
119
|
+
File.open(@ami_manifest, "w") { |f| f.write(ami_manifest) }
|
120
|
+
end
|
121
|
+
|
122
|
+
def upload_to_bucket(previous_deliverables, permissions = 'private')
|
123
|
+
register_deliverable(
|
124
|
+
:package => "#{@appliance_config.name}-#{@appliance_config.version}.#{@appliance_config.release}-#{@appliance_config.os.name}-#{@appliance_config.os.version}-#{@appliance_config.hardware.arch}-#{current_platform}.tgz"
|
125
|
+
)
|
126
|
+
|
127
|
+
# quick and dirty workaround to use @deliverables[:package] later in code
|
128
|
+
FileUtils.mv(@target_deliverables[:package], @deliverables[:package]) if File.exists?(@target_deliverables[:package])
|
129
|
+
|
130
|
+
PackageHelper.new(@config, @appliance_config, :log => @log, :exec_helper => @exec_helper).package(File.dirname(previous_deliverables[:disk]), @deliverables[:package])
|
131
|
+
|
132
|
+
remote_path = "#{s3_path(@plugin_config['path'])}#{File.basename(@deliverables[:package])}"
|
133
|
+
size_b = File.size(@deliverables[:package])
|
134
|
+
|
135
|
+
key = bucket(true, permissions).key(remote_path.gsub(/^\//, '').gsub(/\/\//, ''))
|
136
|
+
|
137
|
+
unless key.exists? or @plugin_config['overwrite']
|
138
|
+
@log.info "Uploading #{File.basename(@deliverables[:package])} (#{size_b/1024/1024}MB) to '#{@plugin_config['bucket']}#{remote_path}' path..."
|
139
|
+
key.put(open(@deliverables[:package]), permissions, :server => REGION_OPTIONS[@plugin_config['region']][:endpoint])
|
140
|
+
@log.info "Appliance #{@appliance_config.name} uploaded to S3."
|
141
|
+
else
|
142
|
+
@log.info "File '#{@plugin_config['bucket']}#{remote_path}' already uploaded, skipping."
|
143
|
+
end
|
144
|
+
|
145
|
+
@s3.close_connection
|
146
|
+
end
|
147
|
+
|
148
|
+
def bucket(create_if_missing = true, permissions = 'private')
|
149
|
+
@s3 ||= Aws::S3.new(@plugin_config['access_key'], @plugin_config['secret_access_key'], :connection_mode => :single, :logger => @log, :server => REGION_OPTIONS[@plugin_config['region']][:endpoint])
|
150
|
+
@s3.bucket(@plugin_config['bucket'], create_if_missing, permissions, :location => REGION_OPTIONS[@plugin_config['region']][:location])
|
151
|
+
end
|
152
|
+
|
153
|
+
def bundle_image(deliverables)
|
154
|
+
if @plugin_config['snapshot']
|
155
|
+
@log.debug "Removing bundled image from local disk..."
|
156
|
+
FileUtils.rm_rf(@ami_build_dir)
|
157
|
+
end
|
158
|
+
|
159
|
+
return if File.exists?(@ami_build_dir)
|
160
|
+
|
161
|
+
@log.info "Bundling AMI..."
|
162
|
+
|
163
|
+
FileUtils.mkdir_p(@ami_build_dir)
|
164
|
+
|
165
|
+
@exec_helper.execute("euca-bundle-image --ec2cert #{File.dirname(__FILE__)}/src/cert-ec2.pem -i #{deliverables[:disk]} --kernel #{REGION_OPTIONS[@plugin_config['region']][:kernel][@appliance_config.hardware.base_arch][:aki]} -c #{@plugin_config['cert_file']} -k #{@plugin_config['key_file']} -u #{@plugin_config['account_number']} -r #{@appliance_config.hardware.base_arch} -d #{@ami_build_dir}", :redacted => [@plugin_config['account_number'], @plugin_config['key_file'], @plugin_config['cert_file']])
|
166
|
+
|
167
|
+
@log.info "Bundling AMI finished."
|
168
|
+
end
|
169
|
+
|
170
|
+
def upload_image(ami_dir)
|
171
|
+
bucket # this will create the bucket if needed
|
172
|
+
@log.info "Uploading #{@appliance_config.name} AMI to bucket '#{@plugin_config['bucket']}'..."
|
173
|
+
|
174
|
+
@exec_helper.execute("euca-upload-bundle -U #{@plugin_config['url'].nil? ? "http://#{REGION_OPTIONS[@plugin_config['region']][:endpoint]}" : @plugin_config['url']} -b #{@plugin_config['bucket']}/#{ami_dir} -m #{@ami_manifest} -a #{@plugin_config['access_key']} -s #{@plugin_config['secret_access_key']}", :redacted => [@plugin_config['access_key'], @plugin_config['secret_access_key']])
|
175
|
+
end
|
176
|
+
|
177
|
+
def register_image(ami_manifest_key)
|
178
|
+
info = ami_info(ami_manifest_key)
|
179
|
+
|
180
|
+
if info
|
181
|
+
@log.info "Image for #{@appliance_config.name} is registered under id: #{info.imageId} (region: #{@plugin_config['region']})."
|
182
|
+
else
|
183
|
+
info = @ec2.register_image(:image_location => "#{@plugin_config['bucket']}/#{ami_manifest_key}")
|
184
|
+
@log.info "Image for #{@appliance_config.name} successfully registered under id: #{info.imageId} (region: #{@plugin_config['region']})."
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def ami_info(ami_manifest_key)
|
189
|
+
ami_info = nil
|
190
|
+
|
191
|
+
images = @ec2.describe_images(:owner_id => @plugin_config['account_number']).imagesSet
|
192
|
+
|
193
|
+
return nil if images.nil?
|
194
|
+
|
195
|
+
for image in images.item do
|
196
|
+
ami_info = image if (image.imageLocation.eql?("#{@plugin_config['bucket']}/#{ami_manifest_key}"))
|
197
|
+
end
|
198
|
+
|
199
|
+
ami_info
|
200
|
+
end
|
201
|
+
|
202
|
+
def s3_path(path)
|
203
|
+
return '' if path == '/'
|
204
|
+
|
205
|
+
"#{path.gsub(/^(\/)*/, '').gsub(/(\/)*$/, '')}/"
|
206
|
+
end
|
207
|
+
|
208
|
+
def ami_key(appliance_name, path)
|
209
|
+
base_path = "#{s3_path(path)}#{appliance_name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}"
|
210
|
+
|
211
|
+
return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot']
|
212
|
+
|
213
|
+
snapshot = 1
|
214
|
+
|
215
|
+
while s3_object_exists?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}/")
|
216
|
+
snapshot += 1
|
217
|
+
end
|
218
|
+
|
219
|
+
"#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}"
|
220
|
+
end
|
221
|
+
|
222
|
+
def s3_object_exists?(path)
|
223
|
+
@log.trace "Checking if '#{path}' path exists in #{@plugin_config['bucket']}..."
|
224
|
+
|
225
|
+
begin
|
226
|
+
b = bucket(false)
|
227
|
+
# Retrieve only one or no keys (if bucket is empty), throw an exception if bucket doesn't exists
|
228
|
+
b.keys('max-keys' => 1)
|
229
|
+
|
230
|
+
if b.key(path).exists?
|
231
|
+
@log.trace "Path exists!"
|
232
|
+
return true
|
233
|
+
end
|
234
|
+
rescue
|
235
|
+
end
|
236
|
+
@log.trace "Path doesn't exist!"
|
237
|
+
false
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
plugin :class => BoxGrinder::S3Plugin, :type => :delivery, :name => :s3, :full_name => "Amazon Simple Storage Service (Amazon S3)", :types => [:s3, :cloudfront, :ami]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDzjCCAzegAwIBAgIJALDnZV+lpZdSMA0GCSqGSIb3DQEBBQUAMIGhMQswCQYD
|
3
|
+
VQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRv
|
4
|
+
d24xJzAlBgNVBAoTHkFtYXpvbiBEZXZlbG9wbWVudCBDZW50cmUgKFNBKTEMMAoG
|
5
|
+
A1UECxMDQUVTMREwDwYDVQQDEwhBRVMgVGVzdDEdMBsGCSqGSIb3DQEJARYOYWVz
|
6
|
+
QGFtYXpvbi5jb20wHhcNMDUwODA5MTYwMTA5WhcNMDYwODA5MTYwMTA5WjCBoTEL
|
7
|
+
MAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
|
8
|
+
ZSBUb3duMScwJQYDVQQKEx5BbWF6b24gRGV2ZWxvcG1lbnQgQ2VudHJlIChTQSkx
|
9
|
+
DDAKBgNVBAsTA0FFUzERMA8GA1UEAxMIQUVTIFRlc3QxHTAbBgkqhkiG9w0BCQEW
|
10
|
+
DmFlc0BhbWF6b24uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8v/X5
|
11
|
+
zZv8CAVfNmvBM0br/RUcf1wU8xC5d2otFQQsQKB3qiWoj3oHeOWskOlTPFVZ8N+/
|
12
|
+
hEaMjyOUkg2+g6XEagCQtFCEBzUVoMjiQIBPiWj5CWkFtlav2zt33LZ0ErTND4xl
|
13
|
+
j7FQFqbaytHU9xuQcFO2p12bdITiBs5Kwoi9bQIDAQABo4IBCjCCAQYwHQYDVR0O
|
14
|
+
BBYEFPQnsX1kDVzPtX+38ACV8RhoYcw8MIHWBgNVHSMEgc4wgcuAFPQnsX1kDVzP
|
15
|
+
tX+38ACV8RhoYcw8oYGnpIGkMIGhMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2Vz
|
16
|
+
dGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xJzAlBgNVBAoTHkFtYXpvbiBE
|
17
|
+
ZXZlbG9wbWVudCBDZW50cmUgKFNBKTEMMAoGA1UECxMDQUVTMREwDwYDVQQDEwhB
|
18
|
+
RVMgVGVzdDEdMBsGCSqGSIb3DQEJARYOYWVzQGFtYXpvbi5jb22CCQCw52VfpaWX
|
19
|
+
UjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAJJlWll4uGlrqBzeIw7u
|
20
|
+
M3RvomlxMESwGKb9gI+ZeORlnHAyZxvd9XngIcjPuU+8uc3wc10LRQUCn45a5hFs
|
21
|
+
zaCp9BSewLCCirn6awZn2tP8JlagSbjrN9YShStt8S3S/Jj+eBoRvc7jJnmEeMkx
|
22
|
+
O0wHOzp5ZHRDK7tGULD6jCfU
|
23
|
+
-----END CERTIFICATE-----
|