boxgrinder-build 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,38 +17,39 @@
17
17
  # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
18
 
19
19
  require 'fileutils'
20
+ require 'boxgrinder-core/helpers/log-helper'
20
21
  require 'boxgrinder-build/helpers/guestfs-helper'
21
22
 
22
23
  module BoxGrinder
23
24
  class ImageHelper
24
25
  def initialize(config, appliance_config, options = {})
25
- @config = config
26
- @appliance_config = appliance_config
26
+ @config = config
27
+ @appliance_config = appliance_config
27
28
 
28
- @log = options[:log] || Logger.new(STDOUT)
29
- @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
29
+ @log = options[:log] || LogHelper.new
30
+ @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
30
31
  end
31
32
 
32
33
  def mount_image(disk, mount_dir)
33
- offsets = calculate_disk_offsets(disk)
34
+ offsets = calculate_disk_offsets(disk)
34
35
 
35
36
  @log.debug "Mounting image #{File.basename(disk)} in #{mount_dir}..."
36
37
  FileUtils.mkdir_p(mount_dir)
37
38
 
38
- mounts = {}
39
+ mounts = {}
39
40
 
40
41
  offsets.each do |offset|
41
- loop_device = get_loop_device
42
- @exec_helper.execute("losetup -o #{offset.to_s} #{loop_device} #{disk}")
43
- label = @exec_helper.execute("e2label #{loop_device}").strip.chomp.gsub('_', '')
42
+ loop_device = get_loop_device
43
+ @exec_helper.execute("losetup -o #{offset.to_s} #{loop_device} '#{disk}'")
44
+ label = @exec_helper.execute("e2label #{loop_device}").strip.chomp.gsub('_', '')
44
45
  label = '/' if label == ''
45
46
  mounts[label] = loop_device
46
47
  end
47
48
 
48
- @exec_helper.execute("mount #{mounts['/']} #{mount_dir}")
49
+ @exec_helper.execute("mount #{mounts['/']} '#{mount_dir}'")
49
50
 
50
51
  mounts.reject { |key, value| key == '/' }.each do |mount_point, loop_device|
51
- @exec_helper.execute("mount #{loop_device} #{mount_dir}#{mount_point}")
52
+ @exec_helper.execute("mount #{loop_device} '#{mount_dir}#{mount_point}'")
52
53
  end
53
54
 
54
55
  @log.trace "Mounts:\n#{mounts}"
@@ -66,6 +67,33 @@ module BoxGrinder
66
67
  FileUtils.rm_rf(mount_dir)
67
68
  end
68
69
 
70
+ def disk_info(disk)
71
+ YAML.load(@exec_helper.execute("qemu-img info '#{disk}'"))
72
+ end
73
+
74
+ def convert_disk(disk, format, destination)
75
+ @log.debug "Conveting '#{disk}' disk to #{format} format and moving it to '#{destination}'..."
76
+
77
+ unless File.exists?(destination)
78
+ info = disk_info(disk)
79
+
80
+ if info['file format'] == format.to_s
81
+ @exec_helper.execute "cp '#{disk}' '#{destination}'"
82
+ else
83
+
84
+ format_with_options = format.to_s
85
+
86
+ if format == :vmdk
87
+ format_with_options += (`qemu-img --help | grep '\\-6'`.strip.chomp.empty? ? ' -o compat6' : ' -6')
88
+ end
89
+
90
+ @exec_helper.execute "qemu-img convert -f #{info['file format']} -O #{format_with_options} '#{disk}' '#{destination}'"
91
+ end
92
+ else
93
+ @log.debug "Destination already exists, skipping disk conversion."
94
+ end
95
+ end
96
+
69
97
  def get_loop_device
70
98
  begin
71
99
  loop_device = @exec_helper.execute("losetup -f 2>&1").strip
@@ -80,8 +108,8 @@ module BoxGrinder
80
108
  @log.debug "Calculating offsets for '#{File.basename(disk)}' disk..."
81
109
  loop_device = get_loop_device
82
110
 
83
- @exec_helper.execute("losetup #{loop_device} #{disk}")
84
- offsets = @exec_helper.execute("parted #{loop_device} 'unit B print' | grep -e '^ [0-9]' | awk '{ print $2 }'").scan(/\d+/)
111
+ @exec_helper.execute("losetup #{loop_device} '#{disk}'")
112
+ offsets = @exec_helper.execute("parted #{loop_device} 'unit B print' | grep -e '^ [0-9]' | awk '{ print $2 }'").scan(/\d+/)
85
113
  # wait one secont before freeing loop device
86
114
  sleep 1
87
115
  @exec_helper.execute("losetup -d #{loop_device}")
@@ -93,21 +121,21 @@ module BoxGrinder
93
121
 
94
122
  def create_disk(disk, size)
95
123
  @log.trace "Preparing disk..."
96
- @exec_helper.execute "dd if=/dev/zero of=#{disk} bs=1 count=0 seek=#{size * 1024}M"
124
+ @exec_helper.execute "dd if=/dev/zero of='#{disk}' bs=1 count=0 seek=#{size * 1024}M"
97
125
  @log.trace "Disk prepared"
98
126
  end
99
127
 
100
- def create_filesystem(disk, options = {})
128
+ def create_filesystem(loop_device, options = {})
101
129
  options = {
102
- :type => @appliance_config.hardware.partitions['/']['type'],
103
- :label => '/'
130
+ :type => @appliance_config.hardware.partitions['/']['type'],
131
+ :label => '/'
104
132
  }.merge(options)
105
133
 
106
134
  @log.trace "Creating filesystem..."
107
135
 
108
136
  case options[:type]
109
137
  when 'ext3', 'ext4'
110
- @exec_helper.execute "mke2fs -T #{options[:type]} -L '#{options[:label]}' -F #{disk}"
138
+ @exec_helper.execute "mke2fs -T #{options[:type]} -L '#{options[:label]}' -F #{loop_device}"
111
139
  else
112
140
  raise "Unsupported filesystem specified: #{options[:type]}"
113
141
  end
@@ -117,12 +145,12 @@ module BoxGrinder
117
145
 
118
146
  def sync_files(from_dir, to_dir)
119
147
  @log.debug "Syncing files between #{from_dir} and #{to_dir}..."
120
- @exec_helper.execute "rsync -Xura #{from_dir}/* #{to_dir}"
148
+ @exec_helper.execute "rsync -Xura #{from_dir.gsub(' ', '\ ')}/* '#{to_dir}'"
121
149
  @log.debug "Sync finished."
122
150
  end
123
151
 
124
152
  def customize(disk_path)
125
- GuestFSHelper.new(disk_path, :log => @log).customize do |guestfs, guestfs_helper|
153
+ GuestFSHelper.new(disk_path, :log => @log).customize(:ide_disk => ((@appliance_config.os.name == 'rhel' or @appliance_config.os.name == 'centos') and @appliance_config.os.version == '5') ? true : false) do |guestfs, guestfs_helper|
126
154
  yield guestfs, guestfs_helper
127
155
  end
128
156
  end
@@ -16,12 +16,12 @@
16
16
  # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
17
  # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
18
 
19
- require 'logger'
19
+ require 'boxgrinder-core/helpers/log-helper'
20
20
 
21
21
  module BoxGrinder
22
22
  class LinuxHelper
23
23
  def initialize( options = {} )
24
- @log = options[:log] || Logger.new(STDOUT)
24
+ @log = options[:log] || LogHelper.new
25
25
  end
26
26
 
27
27
  def kernel_version( guestfs )
@@ -16,15 +16,16 @@
16
16
  # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
17
  # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
18
 
19
+ require 'boxgrinder-core/helpers/log-helper'
20
+
19
21
  module BoxGrinder
20
22
  class PackageHelper
21
- def initialize(config, appliance_config, dir, options = {})
22
- @config = config
23
+ def initialize(config, appliance_config, options = {})
24
+ @config = config
23
25
  @appliance_config = appliance_config
24
- @dir = dir
25
26
 
26
- @log = options[:log] || Logger.new(STDOUT)
27
- @exec_helper = options[:exec_helper] || ExecHelper.new({:log => @log})
27
+ @log = options[:log] || LogHelper.new
28
+ @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
28
29
  end
29
30
 
30
31
  def package(dir, package, type = :tar)
@@ -38,17 +39,13 @@ module BoxGrinder
38
39
  case type
39
40
  when :tar
40
41
  package_name = File.basename(package, '.tgz')
41
- symlink = "#{File.dirname(package)}/#{package_name}"
42
+ symlink = "#{File.dirname(package)}/#{package_name}"
42
43
 
43
44
  FileUtils.ln_s(File.expand_path(dir), symlink)
44
-
45
- Dir.chdir(File.dirname(package)) do
46
- @exec_helper.execute "tar -hcvzf #{package_name}.tgz #{package_name}"
47
- end
48
-
45
+ @exec_helper.execute "tar -C '#{File.dirname(package)}' -hcvzf '#{package}' '#{package_name}'"
49
46
  FileUtils.rm(symlink)
50
47
  else
51
- raise "Only tar format is currently supported."
48
+ raise "Specified format: '#{type}' is currently unsupported."
52
49
  end
53
50
 
54
51
  @log.info "Appliance #{@appliance_config.name} packaged."
@@ -16,14 +16,15 @@
16
16
  # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
17
  # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
18
 
19
- require 'boxgrinder-build/managers/plugin-manager'
20
19
  require 'rubygems'
20
+ require 'boxgrinder-build/managers/plugin-manager'
21
+ require 'boxgrinder-core/helpers/log-helper'
21
22
 
22
23
  module BoxGrinder
23
24
  class PluginHelper
24
- def initialize( options = {} )
25
- @log = options[:log] || Logger.new(STDOUT)
26
- @options = options[:options]
25
+ def initialize( config, options = {} )
26
+ @options = config
27
+ @log = options[:log] || LogHelper.new
27
28
  end
28
29
 
29
30
  def load_plugins
@@ -40,27 +41,18 @@ module BoxGrinder
40
41
  self
41
42
  end
42
43
 
43
- def parse_plugin_list
44
- plugins = []
45
-
46
- unless @options.plugins.nil?
47
- plugins = @options.plugins.gsub('\'', '').gsub('"', '').split(',')
48
- plugins.each { |plugin| plugin.chomp!; plugin.strip! }
49
- end
50
-
51
- plugins
52
- end
53
-
54
44
  def read_and_require
55
- plugins = %w(boxgrinder-build-fedora-os-plugin boxgrinder-build-rhel-os-plugin boxgrinder-build-centos-os-plugin boxgrinder-build-ec2-platform-plugin boxgrinder-build-vmware-platform-plugin boxgrinder-build-s3-delivery-plugin boxgrinder-build-sftp-delivery-plugin boxgrinder-build-local-delivery-plugin boxgrinder-build-ebs-delivery-plugin) + parse_plugin_list
45
+ plugins = %w(boxgrinder-build-fedora-os-plugin boxgrinder-build-rhel-os-plugin boxgrinder-build-centos-os-plugin boxgrinder-build-ec2-platform-plugin boxgrinder-build-vmware-platform-plugin boxgrinder-build-virtualbox-platform-plugin boxgrinder-build-s3-delivery-plugin boxgrinder-build-sftp-delivery-plugin boxgrinder-build-local-delivery-plugin boxgrinder-build-ebs-delivery-plugin) + @options.additional_plugins
56
46
 
57
47
  plugins.flatten.each do |plugin|
58
- @log.trace "Requiring plugin '#{plugin}'..."
48
+ @log.trace "Loading plugin '#{plugin}'..."
59
49
 
60
50
  begin
61
51
  require plugin
62
- rescue LoadError
63
- @log.warn "Specified gem: '#{plugin}' wasn't found. Make sure its name is correct, skipping..." unless plugin.match(/^boxgrinder-build-(.*)-plugin/)
52
+ @log.trace "- OK"
53
+ rescue LoadError => e
54
+ @log.trace "- Not found: #{e.message.strip.chomp}"
55
+ @log.warn "Specified plugin: '#{plugin}' wasn't found. Make sure its name is correct, skipping..." unless plugin.match(/^boxgrinder-build-(.*)-plugin/)
64
56
  end
65
57
  end
66
58
  end
@@ -0,0 +1,82 @@
1
+ # Copyright 2010 Red Hat, Inc.
2
+ #
3
+ # This is free software; you can redistribute it and/or modify it
4
+ # under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation; either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # This software is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this software; if not, write to the Free
15
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
16
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
+
18
+ require 'thor'
19
+
20
+ class Thor
21
+ module CoreExt
22
+ class HashWithIndifferentAccess < ::Hash
23
+ def initialize(hash={})
24
+ super()
25
+ hash.each do |key, value|
26
+ self[convert_key(key)] = value
27
+ end
28
+
29
+ to_boolean(self)
30
+ end
31
+
32
+ def to_boolean(h)
33
+ h.each do |k, v|
34
+ if v.is_a?(Hash)
35
+ to_boolean(v)
36
+ next
37
+ end
38
+
39
+ next unless v.is_a?(String)
40
+
41
+ case v
42
+ when /^true$/i then
43
+ h[k] = true
44
+ when /^false$/i then
45
+ h[k] = false
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ module BoxGrinder
54
+ class ThorHelper < Thor
55
+ class << self
56
+ def help(shell)
57
+ boxgrinder_header(shell)
58
+ super(shell)
59
+ end
60
+
61
+ def task_help(shell, task_name)
62
+ boxgrinder_header(shell)
63
+
64
+ help_method = "#{task_name}_help".to_sym
65
+ send(help_method, shell) if respond_to?(help_method) and method(help_method).arity == 1
66
+ super(shell, task_name)
67
+
68
+ shell.say
69
+ end
70
+
71
+ def boxgrinder_header(shell)
72
+ shell.say
73
+ shell.say "BoxGrinder Build:"
74
+ shell.say " A tool for building VM images from simple definition files."
75
+ shell.say
76
+ shell.say "Documentation:"
77
+ shell.say " http://community.jboss.org/docs/DOC-14358"
78
+ shell.say
79
+ end
80
+ end
81
+ end
82
+ end
@@ -20,8 +20,8 @@ require 'singleton'
20
20
 
21
21
  module BoxGrinder
22
22
  module Plugins
23
- def plugin( args )
24
- PluginManager.instance.register_plugin( args )
23
+ def plugin(args)
24
+ PluginManager.instance.register_plugin(args)
25
25
  end
26
26
  end
27
27
  end
@@ -34,11 +34,11 @@ module BoxGrinder
34
34
  include Singleton
35
35
 
36
36
  def initialize
37
- @plugins = { :delivery => {}, :os => {}, :platform => {}}
37
+ @plugins = {:delivery => {}, :os => {}, :platform => {}}
38
38
  end
39
39
 
40
- def register_plugin( info )
41
- validate_plugin_info( info )
40
+ def register_plugin(info)
41
+ validate_plugin_info(info)
42
42
 
43
43
  raise "We already have registered plugin for #{info[:name]}." unless @plugins[info[:name]].nil?
44
44
 
@@ -53,13 +53,13 @@ module BoxGrinder
53
53
  self
54
54
  end
55
55
 
56
- def validate_plugin_info( info )
56
+ def validate_plugin_info(info)
57
57
  raise "No name specified for your plugin" if info[:name].nil?
58
58
  raise "No class specified for your plugin" if info[:class].nil?
59
59
  raise "No type specified for your plugin" if info[:type].nil?
60
60
  end
61
61
 
62
- def initialize_plugin( type, name )
62
+ def initialize_plugin(type, name)
63
63
  plugins = @plugins[type]
64
64
  # this should never happen
65
65
  raise "There are no #{type} plugins." if plugins.nil?
@@ -68,26 +68,11 @@ module BoxGrinder
68
68
 
69
69
  begin
70
70
  plugin = plugin_info[:class].new
71
- rescue => e
72
- raise "Error while initializing #{plugin_info[:class]} plugin.", e
71
+ rescue
72
+ raise "Error while initializing '#{plugin_info[:class].to_s}' plugin."
73
73
  end
74
74
 
75
- [ plugin, plugin_info ]
76
- end
77
-
78
- def plugin_types( type )
79
- types = []
80
-
81
- available_plugins_for_selected_type = @plugins[type]
82
-
83
- unless available_plugins_for_selected_type.nil?
84
- available_plugins_for_selected_type.each_value do |info|
85
- types << info[:types] unless info[:types].nil?
86
- types << info[:name]
87
- end
88
- end
89
-
90
- types.flatten
75
+ [plugin, plugin_info]
91
76
  end
92
77
 
93
78
  attr_reader :plugins
@@ -28,31 +28,31 @@ require 'logger'
28
28
  module BoxGrinder
29
29
  class BasePlugin
30
30
  def initialize
31
- @plugin_config = {}
31
+ @plugin_config = {}
32
32
 
33
- @deliverables = OpenCascade.new
34
- @supported_oses = OpenCascade.new
33
+ @deliverables = OpenCascade.new
34
+ @supported_oses = OpenCascade.new
35
35
  @target_deliverables = OpenCascade.new
36
- @dir = OpenCascade.new
36
+ @dir = OpenCascade.new
37
37
  end
38
38
 
39
39
  def init(config, appliance_config, options = {})
40
- @config = config
41
- @appliance_config = appliance_config
42
- @options = options
43
- @log = options[:log] || Logger.new(STDOUT)
44
- @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
45
- @image_helper = options[:image_helper] || ImageHelper.new(@config, @appliance_config, :log => @log)
46
- @plugin_info = options[:plugin_info]
47
- @previous_plugin_info = options[:previous_plugin_info]
48
- @previous_deliverables = options[:previous_deliverables] || {}
40
+ @config = config
41
+ @appliance_config = appliance_config
42
+ @options = options
43
+ @log = options[:log] || Logger.new(STDOUT)
44
+ @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
45
+ @image_helper = options[:image_helper] || ImageHelper.new(@config, @appliance_config, :log => @log)
46
+ @previous_plugin_info = options[:previous_plugin_info]
47
+ @previous_deliverables = options[:previous_deliverables] || OpenCascade.new
49
48
 
50
- @dir.base = "#{@appliance_config.path.build}/#{@plugin_info[:name]}-plugin"
51
- @dir.tmp = "#{@dir.base}/tmp"
49
+ @plugin_info = options[:plugin_info]
52
50
 
53
- @config_file = "#{ENV['HOME']}/.boxgrinder/plugins/#{@plugin_info[:name]}"
51
+ @dir.base = "#{@appliance_config.path.build}/#{@plugin_info[:name]}-plugin"
52
+ @dir.tmp = "#{@dir.base}/tmp"
54
53
 
55
54
  read_plugin_config
55
+ merge_plugin_config
56
56
 
57
57
  @move_deliverables = true
58
58
  @initialized = true
@@ -67,7 +67,7 @@ module BoxGrinder
67
67
  raise "Please specify deliverables as Hash, not #{deliverable.class}." unless deliverable.is_a?(Hash)
68
68
 
69
69
  deliverable.each do |name, path|
70
- @deliverables[name] = "#{@dir.tmp}/#{path}"
70
+ @deliverables[name] = "#{@dir.tmp}/#{path}"
71
71
  @target_deliverables[name] = "#{@dir.base}/#{path}"
72
72
  end
73
73
  end
@@ -80,6 +80,7 @@ module BoxGrinder
80
80
  end
81
81
 
82
82
  def is_supported_os?
83
+ return true if @supported_oses.empty?
83
84
  return false unless !@supported_oses[@appliance_config.os.name].nil? and @supported_oses[@appliance_config.os.name].include?(@appliance_config.os.version)
84
85
  true
85
86
  end
@@ -108,10 +109,8 @@ module BoxGrinder
108
109
  def validate_plugin_config(fields = [], doc = nil)
109
110
  more_info = doc.nil? ? '' : "See #{doc} for more info"
110
111
 
111
- raise "Not valid configuration file for #{info[:name]} plugin. Please create valid '#{@config_file}' file. #{more_info}" if @plugin_config.nil?
112
-
113
112
  fields.each do |field|
114
- raise "Please specify a valid '#{field}' key in plugin configuration file: '#{@config_file}'. #{more_info}" if @plugin_config[field].nil?
113
+ raise "Please specify a valid '#{field}' key in BoxGrinder configuration file: '#{@config.file}' or use CLI '--#{@plugin_info[:type]}-config #{field}:DATA' argument. #{more_info}" if @plugin_config[field].nil?
115
114
  end
116
115
  end
117
116
 
@@ -120,18 +119,18 @@ module BoxGrinder
120
119
  end
121
120
 
122
121
  def run(*args)
122
+ unless is_supported_os?
123
+ @log.error "#{@plugin_info[:full_name]} plugin supports following operating systems: #{supported_oses}. Your appliance contains #{@appliance_config.os.name} #{@appliance_config.os.version} operating system which is not supported by this plugin, sorry."
124
+ return
125
+ end
126
+
123
127
  FileUtils.rm_rf @dir.tmp
124
128
  FileUtils.mkdir_p @dir.tmp
125
129
 
126
130
  execute(*args)
127
131
 
128
- after_execute
129
- end
132
+ # TODO execute post commands for platform plugins here?
130
133
 
131
- def after_init
132
- end
133
-
134
- def after_execute
135
134
  @deliverables.each do |name, path|
136
135
  @log.trace "Moving '#{path}' deliverable to target destination '#{@target_deliverables[name]}'..."
137
136
  FileUtils.mv(path, @target_deliverables[name])
@@ -140,6 +139,12 @@ module BoxGrinder
140
139
  FileUtils.rm_rf @dir.tmp
141
140
  end
142
141
 
142
+ def after_init
143
+ end
144
+
145
+ def after_execute
146
+ end
147
+
143
148
  def deliverables_exists?
144
149
  raise "You can only check deliverables after the plugin is initialized, please initialize the plugin using init method." if @initialized.nil?
145
150
 
@@ -163,16 +168,28 @@ module BoxGrinder
163
168
  @plugin_config[key] = @plugin_config[key].nil? ? value : @plugin_config[key]
164
169
  end
165
170
 
171
+ # This reads the plugin config from file
166
172
  def read_plugin_config
167
- return unless File.exists?(@config_file)
173
+ return if @config[:plugins].nil? or @config[:plugins][@plugin_info[:name].to_s].nil?
168
174
 
169
- @log.debug "Reading configuration file for #{self.class.name}."
175
+ @log.debug "Reading configuration for #{@plugin_info[:full_name]} plugin."
170
176
 
171
- begin
172
- @plugin_config = YAML.load_file(@config_file)
173
- rescue
174
- raise "An error occurred while reading configuration file '#{@config_file}' for #{self.class.name}. Is it a valid YAML file?"
175
- end
177
+ @plugin_config = @config[:plugins][@plugin_info[:name].to_s]
178
+ end
179
+
180
+ # This merges the plugin config with configuration provided in command line
181
+ def merge_plugin_config
182
+ config =
183
+ case @plugin_info[:type]
184
+ when :os
185
+ @config.os_config
186
+ when :platform
187
+ @config.platform_config
188
+ when :delivery
189
+ @config.delivery_config
190
+ end
191
+
192
+ @plugin_config.merge!(config)
176
193
  end
177
194
  end
178
195
  end