boxgrinder-build 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/LICENSE +165 -0
  2. data/README +4 -0
  3. data/appliances/jeos.appl +22 -0
  4. data/appliances/meta.appl +41 -0
  5. data/bin/boxgrinder +23 -0
  6. data/docs/examples/appliances/appliances-appliance/appliances-appliance.appl +6 -0
  7. data/docs/examples/appliances/appliances-appliance/appliances-appliance.pp +16 -0
  8. data/docs/examples/appliances/minimal-appliance/minimal-appliance.appl +2 -0
  9. data/docs/examples/appliances/minimal-appliance/minimal-appliance.pp +16 -0
  10. data/docs/examples/appliances/mix-appliance/mix-appliance.appl +9 -0
  11. data/docs/examples/appliances/mix-appliance/mix-appliance.pp +16 -0
  12. data/docs/examples/appliances/packages-appliance/packages-appliance.appl +6 -0
  13. data/docs/examples/appliances/packages-appliance/packages-appliance.pp +16 -0
  14. data/docs/node-info/pom.xml +31 -0
  15. data/docs/node-info/src/main/webapp/META-INF/MANIFEST.MF +3 -0
  16. data/docs/node-info/src/main/webapp/WEB-INF/web.xml +7 -0
  17. data/docs/node-info/src/main/webapp/index.jsp +70 -0
  18. data/extras/sign-rpms +12 -0
  19. data/lib/boxgrinder-build/appliance-kickstart.rb +159 -0
  20. data/lib/boxgrinder-build/appliance.rb +52 -0
  21. data/lib/boxgrinder-build/boxgrinder.rb +92 -0
  22. data/lib/boxgrinder-build/erb/appliance.ks.erb +44 -0
  23. data/lib/boxgrinder-build/helpers/appliance-customize-helper.rb +92 -0
  24. data/lib/boxgrinder-build/helpers/aws-helper.rb +75 -0
  25. data/lib/boxgrinder-build/helpers/guestfs-helper.rb +86 -0
  26. data/lib/boxgrinder-build/helpers/rake-helper.rb +71 -0
  27. data/lib/boxgrinder-build/helpers/release-helper.rb +135 -0
  28. data/lib/boxgrinder-build/helpers/ssh-helper.rb +140 -0
  29. data/lib/boxgrinder-build/images/ec2-image.rb +301 -0
  30. data/lib/boxgrinder-build/images/raw-image.rb +137 -0
  31. data/lib/boxgrinder-build/images/vmware-image.rb +214 -0
  32. data/lib/boxgrinder-build/models/ssh-config.rb +44 -0
  33. data/lib/boxgrinder-build/validators/appliance-config-parameter-validator.rb +37 -0
  34. data/lib/boxgrinder-build/validators/appliance-definition-validator.rb +89 -0
  35. data/lib/boxgrinder-build/validators/appliance-dependency-validator.rb +163 -0
  36. data/lib/boxgrinder-build/validators/appliance-validator.rb +84 -0
  37. data/lib/boxgrinder-build/validators/aws-validator.rb +58 -0
  38. data/lib/boxgrinder-build/validators/config-validator.rb +67 -0
  39. data/lib/boxgrinder-build/validators/ssh-validator.rb +35 -0
  40. data/lib/boxgrinder-build/validators/validator.rb +91 -0
  41. data/lib/boxgrinder-build.rb +48 -0
  42. data/lib/progressbar/progressbar.rb +236 -0
  43. data/src/README.vmware +38 -0
  44. data/src/base.repo +4 -0
  45. data/src/base.vmdk +19 -0
  46. data/src/base.vmx +45 -0
  47. data/src/ec2/fstab_32bit +8 -0
  48. data/src/ec2/fstab_64bit +8 -0
  49. data/src/ec2/ifcfg-eth0 +7 -0
  50. data/src/ec2/rc_local +32 -0
  51. data/src/f12/yum.conf +24 -0
  52. data/src/motd.init +21 -0
  53. data/src/oddthesis/RPM-GPG-KEY-oddthesis +30 -0
  54. data/src/oddthesis/oddthesis.repo +23 -0
  55. 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