boxgrinder-build 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +165 -0
- data/README +4 -0
- data/appliances/jeos.appl +22 -0
- data/appliances/meta.appl +41 -0
- data/bin/boxgrinder +23 -0
- data/docs/examples/appliances/appliances-appliance/appliances-appliance.appl +6 -0
- data/docs/examples/appliances/appliances-appliance/appliances-appliance.pp +16 -0
- data/docs/examples/appliances/minimal-appliance/minimal-appliance.appl +2 -0
- data/docs/examples/appliances/minimal-appliance/minimal-appliance.pp +16 -0
- data/docs/examples/appliances/mix-appliance/mix-appliance.appl +9 -0
- data/docs/examples/appliances/mix-appliance/mix-appliance.pp +16 -0
- data/docs/examples/appliances/packages-appliance/packages-appliance.appl +6 -0
- data/docs/examples/appliances/packages-appliance/packages-appliance.pp +16 -0
- data/docs/node-info/pom.xml +31 -0
- data/docs/node-info/src/main/webapp/META-INF/MANIFEST.MF +3 -0
- data/docs/node-info/src/main/webapp/WEB-INF/web.xml +7 -0
- data/docs/node-info/src/main/webapp/index.jsp +70 -0
- data/extras/sign-rpms +12 -0
- data/lib/boxgrinder-build/appliance-kickstart.rb +159 -0
- data/lib/boxgrinder-build/appliance.rb +52 -0
- data/lib/boxgrinder-build/boxgrinder.rb +92 -0
- data/lib/boxgrinder-build/erb/appliance.ks.erb +44 -0
- data/lib/boxgrinder-build/helpers/appliance-customize-helper.rb +92 -0
- data/lib/boxgrinder-build/helpers/aws-helper.rb +75 -0
- data/lib/boxgrinder-build/helpers/guestfs-helper.rb +86 -0
- data/lib/boxgrinder-build/helpers/rake-helper.rb +71 -0
- data/lib/boxgrinder-build/helpers/release-helper.rb +135 -0
- data/lib/boxgrinder-build/helpers/ssh-helper.rb +140 -0
- data/lib/boxgrinder-build/images/ec2-image.rb +301 -0
- data/lib/boxgrinder-build/images/raw-image.rb +137 -0
- data/lib/boxgrinder-build/images/vmware-image.rb +214 -0
- data/lib/boxgrinder-build/models/ssh-config.rb +44 -0
- data/lib/boxgrinder-build/validators/appliance-config-parameter-validator.rb +37 -0
- data/lib/boxgrinder-build/validators/appliance-definition-validator.rb +89 -0
- data/lib/boxgrinder-build/validators/appliance-dependency-validator.rb +163 -0
- data/lib/boxgrinder-build/validators/appliance-validator.rb +84 -0
- data/lib/boxgrinder-build/validators/aws-validator.rb +58 -0
- data/lib/boxgrinder-build/validators/config-validator.rb +67 -0
- data/lib/boxgrinder-build/validators/ssh-validator.rb +35 -0
- data/lib/boxgrinder-build/validators/validator.rb +91 -0
- data/lib/boxgrinder-build.rb +48 -0
- data/lib/progressbar/progressbar.rb +236 -0
- data/src/README.vmware +38 -0
- data/src/base.repo +4 -0
- data/src/base.vmdk +19 -0
- data/src/base.vmx +45 -0
- data/src/ec2/fstab_32bit +8 -0
- data/src/ec2/fstab_64bit +8 -0
- data/src/ec2/ifcfg-eth0 +7 -0
- data/src/ec2/rc_local +32 -0
- data/src/f12/yum.conf +24 -0
- data/src/motd.init +21 -0
- data/src/oddthesis/RPM-GPG-KEY-oddthesis +30 -0
- data/src/oddthesis/oddthesis.repo +23 -0
- metadata +197 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
# JBoss, Home of Professional Open Source
|
2
|
+
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
+
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
+
# full listing of individual contributors.
|
5
|
+
#
|
6
|
+
# This is free software; you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1 of
|
9
|
+
# the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This software is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this software; if not, write to the Free
|
18
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
+
|
21
|
+
require 'net/ssh'
|
22
|
+
require 'net/sftp'
|
23
|
+
require 'progressbar/progressbar'
|
24
|
+
require 'boxgrinder-core/helpers/exec-helper'
|
25
|
+
|
26
|
+
module BoxGrinder
|
27
|
+
class SSHHelper
|
28
|
+
def initialize( ssh_options, options = {} )
|
29
|
+
@ssh_options = ssh_options
|
30
|
+
|
31
|
+
@log = options[:log] || Logger.new(STDOUT)
|
32
|
+
@exec_helper = options[:exec_helper] || ExecHelper.new( :log => @log )
|
33
|
+
end
|
34
|
+
|
35
|
+
def connect
|
36
|
+
@log.info "Connecting to #{@ssh_options['host']}..."
|
37
|
+
@ssh = Net::SSH.start( @ssh_options['host'], @ssh_options['username'], { :password => @ssh_options['password'] } )
|
38
|
+
end
|
39
|
+
|
40
|
+
def connected?
|
41
|
+
return true if !@ssh.nil? and !@ssh.closed?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def disconnect
|
46
|
+
@log.info "Disconnecting from #{@ssh_options['host']}..."
|
47
|
+
@ssh.close if connected?
|
48
|
+
@ssh = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def upload_files( path, files = {} )
|
52
|
+
|
53
|
+
return if files.size == 0
|
54
|
+
|
55
|
+
raise "You're not connected to server" unless connected?
|
56
|
+
|
57
|
+
global_size = 0
|
58
|
+
|
59
|
+
files.each_value do |file|
|
60
|
+
global_size += File.size( file )
|
61
|
+
end
|
62
|
+
|
63
|
+
global_size_kb = global_size / 1024
|
64
|
+
global_size_mb = global_size_kb / 1024
|
65
|
+
|
66
|
+
@log.info "#{files.size} files to upload (#{global_size_mb > 0 ? global_size_mb.to_s + "MB" : global_size_kb > 0 ? global_size_kb.to_s + "kB" : global_size.to_s})"
|
67
|
+
|
68
|
+
@ssh.sftp.connect do |sftp|
|
69
|
+
|
70
|
+
begin
|
71
|
+
sftp.stat!( path )
|
72
|
+
rescue Net::SFTP::StatusException => e
|
73
|
+
raise unless e.code == 2
|
74
|
+
if @ssh_options['sftp_create_path']
|
75
|
+
@ssh.exec!( "mkdir -p #{path}" )
|
76
|
+
else
|
77
|
+
raise
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
nb = 0
|
82
|
+
|
83
|
+
files.each do |key, local|
|
84
|
+
name = File.basename( local )
|
85
|
+
remote = "#{path}/#{key}"
|
86
|
+
size_b = File.size( local )
|
87
|
+
size_kb = size_b / 1024
|
88
|
+
nb_of = "#{nb += 1}/#{files.size}"
|
89
|
+
|
90
|
+
begin
|
91
|
+
sftp.stat!( remote )
|
92
|
+
|
93
|
+
unless @ssh_options['sftp_overwrite']
|
94
|
+
|
95
|
+
local_md5_sum = `md5sum #{local} | awk '{ print $1 }'`.strip
|
96
|
+
remote_md5_sum = @ssh.exec!( "md5sum #{remote} | awk '{ print $1 }'" ).strip
|
97
|
+
|
98
|
+
if (local_md5_sum.eql?( remote_md5_sum ))
|
99
|
+
@log.info "#{nb_of} #{name}: files are identical (md5sum: #{local_md5_sum}), skipping..."
|
100
|
+
next
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
rescue Net::SFTP::StatusException => e
|
105
|
+
raise unless e.code == 2
|
106
|
+
end
|
107
|
+
|
108
|
+
@ssh.exec!( "mkdir -p #{File.dirname( remote ) }" )
|
109
|
+
|
110
|
+
pbar = ProgressBar.new( "#{nb_of} #{name}", size_b )
|
111
|
+
pbar.file_transfer_mode
|
112
|
+
|
113
|
+
sftp.upload!( local, remote ) do |event, uploader, *args|
|
114
|
+
case event
|
115
|
+
when :open then
|
116
|
+
when :put then
|
117
|
+
pbar.set( args[1] )
|
118
|
+
when :close then
|
119
|
+
when :mkdir then
|
120
|
+
when :finish then
|
121
|
+
pbar.finish
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
sftp.setstat(remote, :permissions => @ssh_options['sftp_default_permissions'])
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def createrepo( path, directories = [] )
|
132
|
+
raise "You're not connected to server" unless connected?
|
133
|
+
|
134
|
+
directories.each do |directory|
|
135
|
+
@log.info "Refreshing repository information in #{directory}..."
|
136
|
+
@ssh.exec!( "createrepo #{path}/#{directory}" )
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# JBoss, Home of Professional Open Source
|
2
|
+
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
+
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
+
# full listing of individual contributors.
|
5
|
+
#
|
6
|
+
# This is free software; you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1 of
|
9
|
+
# the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This software is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this software; if not, write to the Free
|
18
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
+
|
21
|
+
require 'rake/tasklib'
|
22
|
+
require 'fileutils'
|
23
|
+
require 'boxgrinder-core/validators/errors'
|
24
|
+
require 'yaml'
|
25
|
+
require 'AWS'
|
26
|
+
require 'aws/s3'
|
27
|
+
require 'boxgrinder-build/helpers/aws-helper'
|
28
|
+
include AWS::S3
|
29
|
+
|
30
|
+
module BoxGrinder
|
31
|
+
class EC2Image < Rake::TaskLib
|
32
|
+
|
33
|
+
def initialize( config, appliance_config, options = {} )
|
34
|
+
@config = config
|
35
|
+
@appliance_config = appliance_config
|
36
|
+
|
37
|
+
@log = options[:log] || Logger.new(STDOUT)
|
38
|
+
@exec_helper = options[:exec_helper] || ExecHelper.new( :log => @log )
|
39
|
+
|
40
|
+
define_tasks
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_tasks
|
44
|
+
directory @appliance_config.path.dir.ec2.build
|
45
|
+
directory @appliance_config.path.dir.ec2.bundle
|
46
|
+
|
47
|
+
# TODO we should depend on actual disk file, not xml I think
|
48
|
+
file @appliance_config.path.file.ec2.disk => [ @appliance_config.path.file.raw.xml, @appliance_config.path.dir.ec2.build ] do
|
49
|
+
convert_image_to_ec2_format
|
50
|
+
end
|
51
|
+
|
52
|
+
file @appliance_config.path.file.ec2.manifest => [ @appliance_config.path.file.ec2.disk, @appliance_config.path.dir.ec2.bundle ] do
|
53
|
+
@aws_helper = AWSHelper.new( @config, @appliance_config )
|
54
|
+
bundle_image
|
55
|
+
end
|
56
|
+
|
57
|
+
task "appliance:#{@appliance_config.name}:ec2:bundle" => [ @appliance_config.path.file.ec2.manifest ]
|
58
|
+
|
59
|
+
task "appliance:#{@appliance_config.name}:ec2:upload" => [ "appliance:#{@appliance_config.name}:ec2:bundle" ] do
|
60
|
+
@aws_helper = AWSHelper.new( @config, @appliance_config )
|
61
|
+
upload_image
|
62
|
+
end
|
63
|
+
|
64
|
+
task "appliance:#{@appliance_config.name}:ec2:register" => [ "appliance:#{@appliance_config.name}:ec2:upload" ] do
|
65
|
+
@aws_helper = AWSHelper.new( @config, @appliance_config )
|
66
|
+
register_image
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "Build #{@appliance_config.simple_name} appliance for Amazon EC2"
|
70
|
+
task "appliance:#{@appliance_config.name}:ec2" => [ @appliance_config.path.file.ec2.disk ]
|
71
|
+
end
|
72
|
+
|
73
|
+
def bundle_image
|
74
|
+
@log.info "Bundling AMI..."
|
75
|
+
|
76
|
+
@exec_helper.execute( "ec2-bundle-image -i #{@appliance_config.path.file.ec2.disk} --kernel #{AWS_DEFAULTS[:kernel_id][@appliance_config.hardware.arch]} --ramdisk #{AWS_DEFAULTS[:ramdisk_id][@appliance_config.hardware.arch]} -c #{@aws_helper.aws_data['cert_file']} -k #{@aws_helper.aws_data['key_file']} -u #{@aws_helper.aws_data['account_number']} -r #{@appliance_config.hardware.arch} -d #{@appliance_config.path.dir.ec2.bundle}" )
|
77
|
+
|
78
|
+
@log.info "Bundling AMI finished."
|
79
|
+
end
|
80
|
+
|
81
|
+
def appliance_already_uploaded?
|
82
|
+
begin
|
83
|
+
bucket = Bucket.find( @aws_helper.aws_data['bucket_name'] )
|
84
|
+
rescue
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
|
88
|
+
manifest_location = @aws_helper.bucket_manifest_key( @appliance_config.name )
|
89
|
+
manifest_location = manifest_location[ manifest_location.index( "/" ) + 1, manifest_location.length ]
|
90
|
+
|
91
|
+
for object in bucket.objects do
|
92
|
+
return true if object.key.eql?( manifest_location )
|
93
|
+
end
|
94
|
+
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def upload_image
|
99
|
+
if appliance_already_uploaded?
|
100
|
+
@log.debug "Image for #{@appliance_config.simple_name} appliance is already uploaded, skipping..."
|
101
|
+
return
|
102
|
+
end
|
103
|
+
|
104
|
+
@log.info "Uploading #{@appliance_config.simple_name} AMI to bucket '#{@aws_helper.aws_data['bucket_name']}'..."
|
105
|
+
|
106
|
+
@exec_helper.execute( "ec2-upload-bundle -b #{@aws_helper.bucket_key( @appliance_config.name )} -m #{@appliance_config.path.file.ec2.manifest} -a #{@aws_helper.aws_data['access_key']} -s #{@aws_helper.aws_data['secret_access_key']} --retry" )
|
107
|
+
end
|
108
|
+
|
109
|
+
def register_image
|
110
|
+
ami_info = @aws_helper.ami_info( @appliance_config.name )
|
111
|
+
|
112
|
+
if ami_info
|
113
|
+
@log.info "Image is registered under id: #{ami_info.imageId}"
|
114
|
+
return
|
115
|
+
else
|
116
|
+
ami_info = @aws_helper.ec2.register_image( :image_location => @aws_helper.bucket_manifest_key( @appliance_config.name ) )
|
117
|
+
@log.info "Image successfully registered under id: #{ami_info.imageId}."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def convert_image_to_ec2_format
|
122
|
+
@log.info "Converting #{@appliance_config.simple_name} appliance image to EC2 format..."
|
123
|
+
|
124
|
+
ec2_disk_mount_dir = "#{@config.dir.build}/#{@appliance_config.appliance_path}/tmp/ec2-#{rand(9999999999).to_s.center(10, rand(9).to_s)}"
|
125
|
+
raw_disk_mount_dir = "#{@config.dir.build}/#{@appliance_config.appliance_path}/tmp/raw-#{rand(9999999999).to_s.center(10, rand(9).to_s)}"
|
126
|
+
|
127
|
+
ec2_prepare_disk
|
128
|
+
ec2_create_filesystem
|
129
|
+
|
130
|
+
raw_disk_offset = calculate_disk_offset( @appliance_config.path.file.raw.disk )
|
131
|
+
|
132
|
+
ec2_loop_device = mount_image(@appliance_config.path.file.ec2.disk, ec2_disk_mount_dir )
|
133
|
+
raw_loop_device = mount_image(@appliance_config.path.file.raw.disk, raw_disk_mount_dir, raw_disk_offset )
|
134
|
+
|
135
|
+
sync_files( raw_disk_mount_dir, ec2_disk_mount_dir )
|
136
|
+
|
137
|
+
umount_image( @appliance_config.path.file.raw.disk, raw_disk_mount_dir, raw_loop_device )
|
138
|
+
umount_image( @appliance_config.path.file.ec2.disk, ec2_disk_mount_dir, ec2_loop_device )
|
139
|
+
|
140
|
+
guestfs_helper = GuestFSHelper.new( @appliance_config.path.file.ec2.disk, :log => @log )
|
141
|
+
guestfs = guestfs_helper.guestfs
|
142
|
+
|
143
|
+
create_devices( guestfs )
|
144
|
+
upload_fstab( guestfs )
|
145
|
+
|
146
|
+
guestfs.mkdir( "/data" ) if @appliance_config.is64bit?
|
147
|
+
|
148
|
+
enable_networking( guestfs )
|
149
|
+
upload_rc_local( guestfs )
|
150
|
+
|
151
|
+
guestfs_helper.rebuild_rpm_database
|
152
|
+
|
153
|
+
install_additional_packages( guestfs )
|
154
|
+
change_configuration( guestfs )
|
155
|
+
|
156
|
+
if @appliance_config.os.name.eql?("fedora") and @appliance_config.os.version.to_s.eql?("12")
|
157
|
+
@log.debug "Downgrading udev package to use in EC2 environment..."
|
158
|
+
guestfs.sh( "yum -y downgrade udev-142" )
|
159
|
+
guestfs.upload( "#{@config.dir.base}/src/f12/yum.conf", "/etc/yum.conf" )
|
160
|
+
@log.debug "Package udev downgraded."
|
161
|
+
end
|
162
|
+
|
163
|
+
guestfs.close
|
164
|
+
|
165
|
+
@log.info "Image converted to EC2 format."
|
166
|
+
end
|
167
|
+
|
168
|
+
def ec2_prepare_disk
|
169
|
+
# TODO add progress bar?
|
170
|
+
@log.debug "Preparing disk for EC2 image..."
|
171
|
+
@exec_helper.execute "dd if=/dev/zero of=#{@appliance_config.path.file.ec2.disk} bs=1 count=0 seek=#{10 * 1024}M"
|
172
|
+
@log.debug "Disk for EC2 image prepared"
|
173
|
+
end
|
174
|
+
|
175
|
+
def ec2_create_filesystem
|
176
|
+
@log.debug "Creating filesystem..."
|
177
|
+
@exec_helper.execute "mkfs.ext3 -F #{@appliance_config.path.file.ec2.disk}"
|
178
|
+
@log.debug "Filesystem created"
|
179
|
+
end
|
180
|
+
|
181
|
+
def calculate_disk_offset( disk )
|
182
|
+
loop_device = get_loop_device
|
183
|
+
|
184
|
+
@exec_helper.execute( "sudo losetup #{loop_device} #{disk}" )
|
185
|
+
offset = @exec_helper.execute("sudo parted -m #{loop_device} 'unit B print' | grep '^1' | awk -F: '{ print $2 }'").strip.chop
|
186
|
+
@exec_helper.execute( "sudo losetup -d #{loop_device}" )
|
187
|
+
|
188
|
+
offset
|
189
|
+
end
|
190
|
+
|
191
|
+
def mount_image( disk, mount_dir, offset = 0 )
|
192
|
+
loop_device = get_loop_device
|
193
|
+
|
194
|
+
@log.debug "Mounting image #{File.basename( disk )} in #{mount_dir} using #{loop_device} with offset #{offset}"
|
195
|
+
FileUtils.mkdir_p( mount_dir )
|
196
|
+
@exec_helper.execute( "sudo losetup -o #{offset.to_s} #{loop_device} #{disk}" )
|
197
|
+
@exec_helper.execute( "sudo mount #{loop_device} -t ext3 #{ mount_dir}")
|
198
|
+
|
199
|
+
loop_device
|
200
|
+
end
|
201
|
+
|
202
|
+
def umount_image( disk, mount_dir, loop_device )
|
203
|
+
@log.debug "Unmounting image #{File.basename( disk )}"
|
204
|
+
@exec_helper.execute( "sudo umount -d #{loop_device}" )
|
205
|
+
FileUtils.rm_rf( mount_dir )
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
def sync_files( from_dir, to_dir )
|
210
|
+
@log.debug "Syncing files between #{from_dir} and #{to_dir}..."
|
211
|
+
@exec_helper.execute "sudo rsync -u -r -a #{from_dir}/* #{to_dir}"
|
212
|
+
@log.debug "Sync finished."
|
213
|
+
end
|
214
|
+
|
215
|
+
def cache_rpms( rpms )
|
216
|
+
for name in rpms.keys
|
217
|
+
cache_file = "#{@config.dir.src_cache}/#{name}"
|
218
|
+
|
219
|
+
if ( ! File.exist?( cache_file ) )
|
220
|
+
FileUtils.mkdir_p( @config.dir.src_cache )
|
221
|
+
@exec_helper.execute( "wget #{rpms[name]} -O #{cache_file}" )
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def create_devices( guestfs )
|
227
|
+
@log.debug "Creating required devices..."
|
228
|
+
guestfs.sh( "/sbin/MAKEDEV -d /dev -x console" )
|
229
|
+
guestfs.sh( "/sbin/MAKEDEV -d /dev -x null" )
|
230
|
+
guestfs.sh( "/sbin/MAKEDEV -d /dev -x zero" )
|
231
|
+
@log.debug "Devices created."
|
232
|
+
end
|
233
|
+
|
234
|
+
def upload_fstab( guestfs )
|
235
|
+
@log.debug "Uploading '/etc/fstab' file..."
|
236
|
+
fstab_file = @appliance_config.is64bit? ? "#{@config.dir.base}/src/ec2/fstab_64bit" : "#{@config.dir.base}/src/ec2/fstab_32bit"
|
237
|
+
guestfs.upload( fstab_file, "/etc/fstab" )
|
238
|
+
@log.debug "'/etc/fstab' file uploaded."
|
239
|
+
end
|
240
|
+
|
241
|
+
# enable networking on default runlevels
|
242
|
+
def enable_networking( guestfs )
|
243
|
+
@log.debug "Enabling networking..."
|
244
|
+
guestfs.sh( "/sbin/chkconfig network on" )
|
245
|
+
guestfs.upload( "#{@config.dir.base}/src/ec2/ifcfg-eth0", "/etc/sysconfig/network-scripts/ifcfg-eth0" )
|
246
|
+
@log.debug "Networking enabled."
|
247
|
+
end
|
248
|
+
|
249
|
+
def upload_rc_local( guestfs )
|
250
|
+
@log.debug "Uploading '/etc/rc.local' file..."
|
251
|
+
rc_local = Tempfile.new('rc_local')
|
252
|
+
rc_local << guestfs.read_file( "/etc/rc.local" ) + File.read( "#{@config.dir.base}/src/ec2/rc_local" )
|
253
|
+
rc_local.flush
|
254
|
+
|
255
|
+
guestfs.upload( rc_local.path, "/etc/rc.local" )
|
256
|
+
|
257
|
+
rc_local.close
|
258
|
+
@log.debug "'/etc/rc.local' file uploaded."
|
259
|
+
end
|
260
|
+
|
261
|
+
def install_additional_packages( guestfs )
|
262
|
+
rpms = {
|
263
|
+
File.basename(AWS_DEFAULTS[:kernel_rpm][@appliance_config.hardware.arch]) => AWS_DEFAULTS[:kernel_rpm][@appliance_config.hardware.arch],
|
264
|
+
"ec2-ami-tools.noarch.rpm" => "http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm"
|
265
|
+
}
|
266
|
+
|
267
|
+
cache_rpms( rpms )
|
268
|
+
|
269
|
+
@log.debug "Installing additional packages (#{rpms.keys.join( ", " )})..."
|
270
|
+
guestfs.mkdir_p("/tmp/rpms")
|
271
|
+
|
272
|
+
for name in rpms.keys
|
273
|
+
cache_file = "#{@config.dir.src_cache}/#{name}"
|
274
|
+
guestfs.upload( cache_file, "/tmp/rpms/#{name}" )
|
275
|
+
end
|
276
|
+
|
277
|
+
guestfs.sh( "rpm -Uvh --nodeps /tmp/rpms/*.rpm" )
|
278
|
+
guestfs.rm_rf("/tmp/rpms")
|
279
|
+
@log.debug "Additional packages installed."
|
280
|
+
end
|
281
|
+
|
282
|
+
def change_configuration( guestfs )
|
283
|
+
@log.debug "Changing configuration files using augeas..."
|
284
|
+
guestfs.aug_init( "/", 0 )
|
285
|
+
# disable password authentication
|
286
|
+
guestfs.aug_set( "/files/etc/ssh/sshd_config/PasswordAuthentication", "no" )
|
287
|
+
guestfs.aug_save
|
288
|
+
@log.debug "Augeas changes saved."
|
289
|
+
end
|
290
|
+
|
291
|
+
def get_loop_device
|
292
|
+
begin
|
293
|
+
loop_device = @exec_helper.execute("sudo losetup -f 2>&1").strip
|
294
|
+
rescue
|
295
|
+
raise "No free loop devices available, please free at least one. See 'losetup -d' command."
|
296
|
+
end
|
297
|
+
|
298
|
+
loop_device
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# JBoss, Home of Professional Open Source
|
2
|
+
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
+
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
+
# full listing of individual contributors.
|
5
|
+
#
|
6
|
+
# This is free software; you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1 of
|
9
|
+
# the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This software is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this software; if not, write to the Free
|
18
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
+
|
21
|
+
require 'rake/tasklib'
|
22
|
+
require 'yaml'
|
23
|
+
require 'boxgrinder-build/helpers/guestfs-helper'
|
24
|
+
require 'boxgrinder-core/helpers/exec-helper'
|
25
|
+
|
26
|
+
module BoxGrinder
|
27
|
+
class RAWImage < Rake::TaskLib
|
28
|
+
|
29
|
+
def initialize( config, appliance_config, options = {} )
|
30
|
+
@config = config
|
31
|
+
@appliance_config = appliance_config
|
32
|
+
|
33
|
+
@log = options[:log] || Logger.new(STDOUT)
|
34
|
+
@exec_helper = options[:exec_helper] || ExecHelper.new( { :log => @log } )
|
35
|
+
|
36
|
+
@tmp_dir = "#{@config.dir.root}/#{@config.dir.build}/tmp"
|
37
|
+
|
38
|
+
define_tasks
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_tasks
|
42
|
+
desc "Build #{@appliance_config.simple_name} appliance."
|
43
|
+
task "appliance:#{@appliance_config.name}" => [ @appliance_config.path.file.raw.xml, "appliance:#{@appliance_config.name}:validate:dependencies" ]
|
44
|
+
|
45
|
+
directory @tmp_dir
|
46
|
+
|
47
|
+
file @appliance_config.path.file.raw.xml => [ @appliance_config.path.file.raw.kickstart, "appliance:#{@appliance_config.name}:validate:dependencies", @tmp_dir ] do
|
48
|
+
build_raw_image
|
49
|
+
do_post_build_operations
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_raw_image
|
54
|
+
@log.info "Building #{@appliance_config.simple_name} appliance..."
|
55
|
+
|
56
|
+
@exec_helper.execute "sudo PYTHONUNBUFFERED=1 appliance-creator -d -v -t #{@tmp_dir} --cache=#{@config.dir.rpms_cache}/#{@appliance_config.main_path} --config #{@appliance_config.path.file.raw.kickstart} -o #{@appliance_config.path.dir.raw.build} --name #{@appliance_config.name} --vmem #{@appliance_config.hardware.memory} --vcpu #{@appliance_config.hardware.cpus}"
|
57
|
+
|
58
|
+
# fix permissions
|
59
|
+
@exec_helper.execute "sudo chmod 777 #{@appliance_config.path.dir.raw.build_full}"
|
60
|
+
@exec_helper.execute "sudo chmod 666 #{@appliance_config.path.file.raw.disk}"
|
61
|
+
@exec_helper.execute "sudo chmod 666 #{@appliance_config.path.file.raw.xml}"
|
62
|
+
|
63
|
+
@log.info "Appliance #{@appliance_config.simple_name} was built successfully."
|
64
|
+
end
|
65
|
+
|
66
|
+
def do_post_build_operations
|
67
|
+
@log.info "Executing post operations after build..."
|
68
|
+
|
69
|
+
guestfs_helper = GuestFSHelper.new( @appliance_config.path.file.raw.disk, :log => @log )
|
70
|
+
guestfs = guestfs_helper.guestfs
|
71
|
+
|
72
|
+
change_configuration( guestfs )
|
73
|
+
set_motd( guestfs )
|
74
|
+
install_version_files( guestfs )
|
75
|
+
install_repos( guestfs )
|
76
|
+
|
77
|
+
@log.debug "Executing post commands from appliance definition file..."
|
78
|
+
if @appliance_config.post.base.size > 0
|
79
|
+
for cmd in @appliance_config.post.base
|
80
|
+
@log.debug "Executing #{cmd}"
|
81
|
+
guestfs.sh( cmd )
|
82
|
+
end
|
83
|
+
@log.debug "Post commands from appliance definition file executed."
|
84
|
+
else
|
85
|
+
@log.debug "No commands specified, skipping."
|
86
|
+
end
|
87
|
+
|
88
|
+
guestfs.close
|
89
|
+
|
90
|
+
@log.info "Post operations executed."
|
91
|
+
end
|
92
|
+
|
93
|
+
def change_configuration( guestfs )
|
94
|
+
@log.debug "Changing configuration files using augeas..."
|
95
|
+
guestfs.aug_init( "/", 0 )
|
96
|
+
# don't use DNS for SSH
|
97
|
+
guestfs.aug_set( "/files/etc/ssh/sshd_config/UseDNS", "no" )
|
98
|
+
guestfs.aug_save
|
99
|
+
@log.debug "Augeas changes saved."
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_motd( guestfs )
|
103
|
+
@log.debug "Setting up '/etc/motd'..."
|
104
|
+
# set nice banner for SSH
|
105
|
+
motd_file = "/etc/init.d/motd"
|
106
|
+
guestfs.upload( "#{@config.dir.base}/src/motd.init", motd_file )
|
107
|
+
guestfs.sh( "sed -i s/#VERSION#/'#{@appliance_config.version}.#{@appliance_config.release}'/ #{motd_file}" )
|
108
|
+
guestfs.sh( "sed -i s/#APPLIANCE#/'#{@appliance_config.name} appliance'/ #{motd_file}" )
|
109
|
+
|
110
|
+
guestfs.sh( "/bin/chmod +x #{motd_file}" )
|
111
|
+
guestfs.sh( "/sbin/chkconfig --add motd" )
|
112
|
+
@log.debug "'/etc/motd' is nice now."
|
113
|
+
end
|
114
|
+
|
115
|
+
def install_version_files( guestfs )
|
116
|
+
@log.debug "Installing BoxGrinder version files..."
|
117
|
+
guestfs.sh( "echo 'BOXGRINDER_VERSION=#{@config.version_with_release}' > /etc/sysconfig/boxgrinder" )
|
118
|
+
guestfs.sh( "echo 'APPLIANCE_NAME=#{@appliance_config.name}' >> /etc/sysconfig/boxgrinder" )
|
119
|
+
@log.debug "Version files installed."
|
120
|
+
end
|
121
|
+
|
122
|
+
def install_repos( guestfs )
|
123
|
+
@log.debug "Installing repositories from appliance definition file..."
|
124
|
+
@appliance_config.repos.each do |repo|
|
125
|
+
@log.debug "Installing #{repo['name']} repo..."
|
126
|
+
repo_file = File.read( "#{@config.dir.base}/src/base.repo").gsub( /#NAME#/, repo['name'] )
|
127
|
+
|
128
|
+
['baseurl', 'mirrorlist'].each do |type|
|
129
|
+
repo_file << ("#{type}=#{repo[type]}") unless repo[type].nil?
|
130
|
+
end
|
131
|
+
|
132
|
+
guestfs.sh("echo '#{repo_file}' > /etc/yum.repos.d/#{repo['name']}.repo")
|
133
|
+
end
|
134
|
+
@log.debug "Repositories installed."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|