boxgrinder-build 0.0.1
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/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
|