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.
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