boxgrinder-build 0.5.1 → 0.6.0
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/CHANGELOG +5 -0
- data/Manifest +0 -26
- data/Rakefile +3 -3
- data/bin/boxgrinder-build +8 -17
- data/boxgrinder-build.gemspec +10 -10
- data/lib/boxgrinder-build/appliance.rb +27 -7
- data/lib/boxgrinder-build/helpers/image-helper.rb +123 -0
- data/lib/boxgrinder-build/helpers/plugin-helper.rb +1 -1
- data/lib/boxgrinder-build/managers/plugin-manager.rb +0 -1
- data/lib/boxgrinder-build/plugins/base-plugin.rb +27 -11
- data/spec/appliance-spec.rb +115 -119
- data/spec/helpers/image-helper-spec.rb +144 -0
- data/spec/helpers/plugin-helper-spec.rb +1 -1
- data/spec/plugins/base-plugin-spec.rb +69 -4
- metadata +20 -8
data/CHANGELOG
CHANGED
data/Manifest
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
CHANGELOG
|
|
2
|
-
LICENSE
|
|
3
|
-
Manifest
|
|
4
|
-
README
|
|
5
|
-
Rakefile
|
|
6
|
-
bin/boxgrinder-build
|
|
7
|
-
lib/boxgrinder-build/appliance.rb
|
|
8
|
-
lib/boxgrinder-build/helpers/appliance-customize-helper.rb
|
|
9
|
-
lib/boxgrinder-build/helpers/augeas-helper.rb
|
|
10
|
-
lib/boxgrinder-build/helpers/guestfs-helper.rb
|
|
11
|
-
lib/boxgrinder-build/helpers/linux-helper.rb
|
|
12
|
-
lib/boxgrinder-build/helpers/package-helper.rb
|
|
13
|
-
lib/boxgrinder-build/helpers/plugin-helper.rb
|
|
14
|
-
lib/boxgrinder-build/managers/plugin-manager.rb
|
|
15
|
-
lib/boxgrinder-build/plugins/base-plugin.rb
|
|
16
|
-
spec/Rakefile
|
|
17
|
-
spec/appliance-spec.rb
|
|
18
|
-
spec/helpers/appliance-customize-helper-spec.rb
|
|
19
|
-
spec/helpers/augeas-helper-spec.rb
|
|
20
|
-
spec/helpers/guestfs-helper-spec.rb
|
|
21
|
-
spec/helpers/linux-helper-spec.rb
|
|
22
|
-
spec/helpers/package-helper-spec.rb
|
|
23
|
-
spec/helpers/plugin-helper-spec.rb
|
|
24
|
-
spec/managers/plugin-manager-spec.rb
|
|
25
|
-
spec/plugins/base-plugin-spec.rb
|
|
26
|
-
spec/rspec/src/appliances/jeos-f13.appl
|
data/Rakefile
CHANGED
|
@@ -4,8 +4,8 @@ Echoe.new("boxgrinder-build") do |p|
|
|
|
4
4
|
p.project = "BoxGrinder Build"
|
|
5
5
|
p.author = "Marek Goldmann"
|
|
6
6
|
p.summary = "A tool for creating appliances from simple plain text files for various virtual environments."
|
|
7
|
-
p.url = "http://www.jboss.org/
|
|
7
|
+
p.url = "http://www.jboss.org/boxgrinder"
|
|
8
8
|
p.email = "info@boxgrinder.org"
|
|
9
9
|
p.ignore_pattern = /^(pkg|doc)|\.svn|CVS|\.bzr|\.DS|\.git|\.log/
|
|
10
|
-
p.runtime_dependencies = ["commander ~>4.0.3", "boxgrinder-core ~>0.0
|
|
11
|
-
end
|
|
10
|
+
p.runtime_dependencies = ["commander ~>4.0.3", "boxgrinder-core ~>0.1.0"]
|
|
11
|
+
end
|
data/bin/boxgrinder-build
CHANGED
|
@@ -21,14 +21,13 @@
|
|
|
21
21
|
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
|
22
22
|
|
|
23
23
|
require 'rubygems'
|
|
24
|
-
require 'boxgrinder-core/helpers/log-helper'
|
|
25
|
-
require 'boxgrinder-build/managers/plugin-manager'
|
|
26
|
-
require 'boxgrinder-build/helpers/plugin-helper'
|
|
27
|
-
require 'boxgrinder-build/appliance'
|
|
28
24
|
|
|
29
|
-
gem 'boxgrinder-core', '>= 0.0
|
|
25
|
+
gem 'boxgrinder-core', '>= 0.1.0'
|
|
30
26
|
gem 'commander', '>= 4.0.3'
|
|
31
27
|
|
|
28
|
+
require 'boxgrinder-core/helpers/log-helper'
|
|
29
|
+
require 'boxgrinder-build/appliance'
|
|
30
|
+
|
|
32
31
|
#$stderr.reopen($stdout)
|
|
33
32
|
|
|
34
33
|
if Process.uid != 0
|
|
@@ -39,9 +38,9 @@ end
|
|
|
39
38
|
require 'commander/import'
|
|
40
39
|
|
|
41
40
|
program :name, 'BoxGrinder Build'
|
|
42
|
-
program :version, '0.
|
|
41
|
+
program :version, '0.6.0'
|
|
43
42
|
program :description, "A tool for building VM images from simple definition files."
|
|
44
|
-
program :help, '
|
|
43
|
+
program :help, 'Homepage', 'http://www.jboss.org/boxgrinder/build.html'
|
|
45
44
|
program :help, 'Documentation', 'http://community.jboss.org/docs/DOC-14358'
|
|
46
45
|
program :help, 'Examples', "Run 'boxgrinder-build -h build' for more info about syntax."
|
|
47
46
|
default_command :build
|
|
@@ -49,7 +48,7 @@ default_command :build
|
|
|
49
48
|
$log_level = :info
|
|
50
49
|
|
|
51
50
|
global_option('-V', '--verbose', TrueClass, "Prints debug information while building. Default: false.") { $log_level = :debug}
|
|
52
|
-
global_option('-W', '--
|
|
51
|
+
global_option('-W', '--very-verbose', TrueClass, "Prints trace information while building. Default: false.") { $log_level = :trace}
|
|
53
52
|
|
|
54
53
|
module BoxGrinder
|
|
55
54
|
command :build do |c|
|
|
@@ -77,15 +76,7 @@ module BoxGrinder
|
|
|
77
76
|
|
|
78
77
|
raise "Appliance definition file '#{appliance_definition_file}' could not be found" unless File.exists?( appliance_definition_file )
|
|
79
78
|
|
|
80
|
-
log
|
|
81
|
-
|
|
82
|
-
PluginHelper.new( :options => options, :log => log ).load_plugins
|
|
83
|
-
PluginManager.instance.log = log
|
|
84
|
-
|
|
85
|
-
raise "Not known platform: #{options.platform}. #{PluginManager.instance.plugins[:platform].empty? ? 'There are no platform plugins, please install one or more.' : "Available types: #{PluginManager.instance.plugin_types( :platform ).join(', ')}."}" if PluginManager.instance.plugins[:platform][options.platform].nil? and options.platform != :none
|
|
86
|
-
raise "Not known delivery type: #{options.delivery}. #{PluginManager.instance.plugins[:delivery].empty? ? 'There are no delivery plugins, please install one or more.' : "Available types: #{PluginManager.instance.plugin_types( :delivery ).join(', ')}."}" if PluginManager.instance.plugin_types( :delivery ).include?(options.delivery).nil? and options.delivery != :none
|
|
87
|
-
|
|
88
|
-
Appliance.new( appliance_definition_file, :options => options, :log => log ).create
|
|
79
|
+
Appliance.new( appliance_definition_file, :options => options, :log => LogHelper.new( :threshold => $log_level ) ).create
|
|
89
80
|
end
|
|
90
81
|
end
|
|
91
82
|
end
|
data/boxgrinder-build.gemspec
CHANGED
|
@@ -2,37 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = %q{boxgrinder-build}
|
|
5
|
-
s.version = "0.
|
|
5
|
+
s.version = "0.6.0"
|
|
6
6
|
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
|
8
8
|
s.authors = ["Marek Goldmann"]
|
|
9
|
-
s.date = %q{2010-
|
|
9
|
+
s.date = %q{2010-10-12}
|
|
10
10
|
s.default_executable = %q{boxgrinder-build}
|
|
11
11
|
s.description = %q{A tool for creating appliances from simple plain text files for various virtual environments.}
|
|
12
12
|
s.email = %q{info@boxgrinder.org}
|
|
13
13
|
s.executables = ["boxgrinder-build"]
|
|
14
|
-
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "bin/boxgrinder-build", "lib/boxgrinder-build/appliance.rb", "lib/boxgrinder-build/helpers/appliance-customize-helper.rb", "lib/boxgrinder-build/helpers/augeas-helper.rb", "lib/boxgrinder-build/helpers/guestfs-helper.rb", "lib/boxgrinder-build/helpers/linux-helper.rb", "lib/boxgrinder-build/helpers/package-helper.rb", "lib/boxgrinder-build/helpers/plugin-helper.rb", "lib/boxgrinder-build/managers/plugin-manager.rb", "lib/boxgrinder-build/plugins/base-plugin.rb"]
|
|
15
|
-
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "bin/boxgrinder-build", "lib/boxgrinder-build/appliance.rb", "lib/boxgrinder-build/helpers/appliance-customize-helper.rb", "lib/boxgrinder-build/helpers/augeas-helper.rb", "lib/boxgrinder-build/helpers/guestfs-helper.rb", "lib/boxgrinder-build/helpers/linux-helper.rb", "lib/boxgrinder-build/helpers/package-helper.rb", "lib/boxgrinder-build/helpers/plugin-helper.rb", "lib/boxgrinder-build/managers/plugin-manager.rb", "lib/boxgrinder-build/plugins/base-plugin.rb", "spec/Rakefile", "spec/appliance-spec.rb", "spec/helpers/appliance-customize-helper-spec.rb", "spec/helpers/augeas-helper-spec.rb", "spec/helpers/guestfs-helper-spec.rb", "spec/helpers/linux-helper-spec.rb", "spec/helpers/package-helper-spec.rb", "spec/helpers/plugin-helper-spec.rb", "spec/managers/plugin-manager-spec.rb", "spec/plugins/base-plugin-spec.rb", "spec/rspec/src/appliances/jeos-f13.appl", "boxgrinder-build.gemspec"]
|
|
16
|
-
s.homepage = %q{http://www.jboss.org/
|
|
14
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "bin/boxgrinder-build", "lib/boxgrinder-build/appliance.rb", "lib/boxgrinder-build/helpers/appliance-customize-helper.rb", "lib/boxgrinder-build/helpers/augeas-helper.rb", "lib/boxgrinder-build/helpers/guestfs-helper.rb", "lib/boxgrinder-build/helpers/image-helper.rb", "lib/boxgrinder-build/helpers/linux-helper.rb", "lib/boxgrinder-build/helpers/package-helper.rb", "lib/boxgrinder-build/helpers/plugin-helper.rb", "lib/boxgrinder-build/managers/plugin-manager.rb", "lib/boxgrinder-build/plugins/base-plugin.rb"]
|
|
15
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "bin/boxgrinder-build", "lib/boxgrinder-build/appliance.rb", "lib/boxgrinder-build/helpers/appliance-customize-helper.rb", "lib/boxgrinder-build/helpers/augeas-helper.rb", "lib/boxgrinder-build/helpers/guestfs-helper.rb", "lib/boxgrinder-build/helpers/image-helper.rb", "lib/boxgrinder-build/helpers/linux-helper.rb", "lib/boxgrinder-build/helpers/package-helper.rb", "lib/boxgrinder-build/helpers/plugin-helper.rb", "lib/boxgrinder-build/managers/plugin-manager.rb", "lib/boxgrinder-build/plugins/base-plugin.rb", "spec/Rakefile", "spec/appliance-spec.rb", "spec/helpers/appliance-customize-helper-spec.rb", "spec/helpers/augeas-helper-spec.rb", "spec/helpers/guestfs-helper-spec.rb", "spec/helpers/image-helper-spec.rb", "spec/helpers/linux-helper-spec.rb", "spec/helpers/package-helper-spec.rb", "spec/helpers/plugin-helper-spec.rb", "spec/managers/plugin-manager-spec.rb", "spec/plugins/base-plugin-spec.rb", "spec/rspec/src/appliances/jeos-f13.appl", "boxgrinder-build.gemspec"]
|
|
16
|
+
s.homepage = %q{http://www.jboss.org/boxgrinder}
|
|
17
17
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Boxgrinder-build", "--main", "README"]
|
|
18
18
|
s.require_paths = ["lib"]
|
|
19
19
|
s.rubyforge_project = %q{BoxGrinder Build}
|
|
20
|
-
s.rubygems_version = %q{1.3.
|
|
20
|
+
s.rubygems_version = %q{1.3.7}
|
|
21
21
|
s.summary = %q{A tool for creating appliances from simple plain text files for various virtual environments.}
|
|
22
22
|
|
|
23
23
|
if s.respond_to? :specification_version then
|
|
24
24
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
25
25
|
s.specification_version = 3
|
|
26
26
|
|
|
27
|
-
if Gem::Version.new(Gem::
|
|
27
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
28
28
|
s.add_runtime_dependency(%q<commander>, ["~> 4.0.3"])
|
|
29
|
-
s.add_runtime_dependency(%q<boxgrinder-core>, ["~> 0.0
|
|
29
|
+
s.add_runtime_dependency(%q<boxgrinder-core>, ["~> 0.1.0"])
|
|
30
30
|
else
|
|
31
31
|
s.add_dependency(%q<commander>, ["~> 4.0.3"])
|
|
32
|
-
s.add_dependency(%q<boxgrinder-core>, ["~> 0.0
|
|
32
|
+
s.add_dependency(%q<boxgrinder-core>, ["~> 0.1.0"])
|
|
33
33
|
end
|
|
34
34
|
else
|
|
35
35
|
s.add_dependency(%q<commander>, ["~> 4.0.3"])
|
|
36
|
-
s.add_dependency(%q<boxgrinder-core>, ["~> 0.0
|
|
36
|
+
s.add_dependency(%q<boxgrinder-core>, ["~> 0.1.0"])
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -22,6 +22,8 @@ require 'boxgrinder-core/models/appliance-config'
|
|
|
22
22
|
require 'boxgrinder-core/models/config'
|
|
23
23
|
require 'boxgrinder-core/helpers/appliance-helper'
|
|
24
24
|
require 'boxgrinder-core/helpers/appliance-config-helper'
|
|
25
|
+
require 'boxgrinder-build/helpers/plugin-helper'
|
|
26
|
+
require 'boxgrinder-build/managers/plugin-manager'
|
|
25
27
|
require 'boxgrinder-core/validators/appliance-config-validator'
|
|
26
28
|
|
|
27
29
|
module BoxGrinder
|
|
@@ -38,19 +40,23 @@ module BoxGrinder
|
|
|
38
40
|
@config.version.release = nil
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
def
|
|
43
|
+
def read_and_validate_definition
|
|
42
44
|
appliance_configs, appliance_config = ApplianceHelper.new( :log => @log ).read_definitions( @appliance_definition_file )
|
|
43
45
|
appliance_config_helper = ApplianceConfigHelper.new( appliance_configs )
|
|
44
46
|
|
|
45
47
|
@appliance_config = appliance_config_helper.merge(appliance_config.clone.init_arch).initialize_paths
|
|
46
48
|
|
|
47
|
-
ApplianceConfigValidator.new( @appliance_config
|
|
49
|
+
ApplianceConfigValidator.new( @appliance_config ).validate
|
|
50
|
+
end
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
def remove_old_builds
|
|
53
|
+
@log.info "Removing previous builds for #{@appliance_config.name} appliance..."
|
|
54
|
+
FileUtils.rm_rf( @appliance_config.path.build )
|
|
55
|
+
@log.debug "Previous builds removed."
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def execute_plugin_chain
|
|
59
|
+
@log.info "Building '#{@appliance_config.name}' appliance for #{@appliance_config.hardware.arch} architecture."
|
|
54
60
|
|
|
55
61
|
base_plugin_output = execute_os_plugin
|
|
56
62
|
platform_plugin_output = execute_platform_plugin( base_plugin_output )
|
|
@@ -58,7 +64,17 @@ module BoxGrinder
|
|
|
58
64
|
execute_delivery_plugin( platform_plugin_output )
|
|
59
65
|
end
|
|
60
66
|
|
|
67
|
+
def create
|
|
68
|
+
PluginHelper.new( :options => @options, :log => @log ).load_plugins
|
|
69
|
+
|
|
70
|
+
read_and_validate_definition
|
|
71
|
+
remove_old_builds if @options.force
|
|
72
|
+
execute_plugin_chain
|
|
73
|
+
end
|
|
74
|
+
|
|
61
75
|
def execute_os_plugin
|
|
76
|
+
raise "No operating system plugins installed. Install one or more operating system plugin. See http://community.jboss.org/docs/DOC-15081 and http://community.jboss.org/docs/DOC-15214 for more info" if PluginManager.instance.plugins[:os].empty?
|
|
77
|
+
|
|
62
78
|
os_plugin, os_plugin_info = PluginManager.instance.initialize_plugin(:os, @appliance_config.os.name.to_sym )
|
|
63
79
|
os_plugin.init( @config, @appliance_config, :log => @log, :plugin_info => os_plugin_info )
|
|
64
80
|
|
|
@@ -80,6 +96,8 @@ module BoxGrinder
|
|
|
80
96
|
return previous_plugin_output
|
|
81
97
|
end
|
|
82
98
|
|
|
99
|
+
raise "No platform plugins installed. Install one or more platform plugin. See http://community.jboss.org/docs/DOC-15081 and http://community.jboss.org/docs/DOC-15214 for more info" if PluginManager.instance.plugins[:platform].empty?
|
|
100
|
+
|
|
83
101
|
platform_plugin, platform_plugin_info = PluginManager.instance.initialize_plugin(:platform, @options.platform )
|
|
84
102
|
platform_plugin.init( @config, @appliance_config, :log => @log, :plugin_info => platform_plugin_info, :previous_plugin_info => previous_plugin_output[:plugin_info], :previous_deliverables => previous_plugin_output[:deliverables] )
|
|
85
103
|
|
|
@@ -101,6 +119,8 @@ module BoxGrinder
|
|
|
101
119
|
return
|
|
102
120
|
end
|
|
103
121
|
|
|
122
|
+
raise "No delivery plugins installed. Install one or more delivery plugin. See http://community.jboss.org/docs/DOC-15081 and http://community.jboss.org/docs/DOC-15214 for more info" if PluginManager.instance.plugins[:delivery].empty?
|
|
123
|
+
|
|
104
124
|
delivery_plugin, delivery_plugin_info = PluginManager.instance.initialize_plugin(:delivery, @options.delivery )
|
|
105
125
|
delivery_plugin.init( @config, @appliance_config, :log => @log, :plugin_info => delivery_plugin_info, :previous_plugin_info => previous_plugin_output[:plugin_info], :previous_deliverables => previous_plugin_output[:deliverables] )
|
|
106
126
|
|
|
@@ -0,0 +1,123 @@
|
|
|
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 'fileutils'
|
|
22
|
+
require 'boxgrinder-build/helpers/guestfs-helper'
|
|
23
|
+
|
|
24
|
+
module BoxGrinder
|
|
25
|
+
class ImageHelper
|
|
26
|
+
def initialize(options = {})
|
|
27
|
+
@log = options[:log] || Logger.new(STDOUT)
|
|
28
|
+
@exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def mount_image(disk, mount_dir)
|
|
32
|
+
offsets = calculate_disk_offsets(disk)
|
|
33
|
+
|
|
34
|
+
@log.debug "Mounting image #{File.basename(disk)} in #{mount_dir}..."
|
|
35
|
+
FileUtils.mkdir_p(mount_dir)
|
|
36
|
+
|
|
37
|
+
mounts = {}
|
|
38
|
+
|
|
39
|
+
offsets.each do |offset|
|
|
40
|
+
loop_device = get_loop_device
|
|
41
|
+
@exec_helper.execute("losetup -o #{offset.to_s} #{loop_device} #{disk}")
|
|
42
|
+
label = @exec_helper.execute("e2label #{loop_device}").strip.chomp.gsub('_', '')
|
|
43
|
+
label = '/' if label == ''
|
|
44
|
+
mounts[label] = loop_device
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@exec_helper.execute("mount #{mounts['/']} -t #{get_filesystem_type(mounts['/'])} #{mount_dir}")
|
|
48
|
+
|
|
49
|
+
mounts.reject { |key, value| key == '/' }.each do |mount_point, loop_device|
|
|
50
|
+
@exec_helper.execute("mount #{loop_device} -t #{get_filesystem_type(loop_device)} #{mount_dir}#{mount_point}")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@log.trace "Mounts:\n#{mounts}"
|
|
54
|
+
|
|
55
|
+
mounts
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_filesystem_type(device, default_type = 'ext3')
|
|
59
|
+
fs_type = @exec_helper.execute("df -T #{device} | tail -1 | awk '{print $2}'")
|
|
60
|
+
return default_type if fs_type.empty? or fs_type == '-'
|
|
61
|
+
fs_type
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def umount_image(disk, mount_dir, mounts)
|
|
65
|
+
@log.debug "Unmounting image '#{File.basename(disk)}'..."
|
|
66
|
+
|
|
67
|
+
mounts.each { |mount_point, loop_device| @exec_helper.execute("umount -d #{loop_device}") unless mount_point == '/' }
|
|
68
|
+
|
|
69
|
+
@exec_helper.execute("umount -d #{mounts['/']}")
|
|
70
|
+
|
|
71
|
+
FileUtils.rm_rf(mount_dir)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def get_loop_device
|
|
75
|
+
begin
|
|
76
|
+
loop_device = @exec_helper.execute("losetup -f 2>&1").strip
|
|
77
|
+
rescue
|
|
78
|
+
raise "No free loop devices available, please free at least one. See 'losetup -d' command."
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
loop_device
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def calculate_disk_offsets(disk)
|
|
85
|
+
@log.debug "Calculating offsets for '#{File.basename(disk)}' disk..."
|
|
86
|
+
loop_device = get_loop_device
|
|
87
|
+
|
|
88
|
+
@exec_helper.execute("losetup #{loop_device} #{disk}")
|
|
89
|
+
offsets = @exec_helper.execute("parted #{loop_device} 'unit B print' | grep -e '^ [0-9]' | awk '{ print $2 }'").scan(/\d+/)
|
|
90
|
+
@exec_helper.execute("losetup -d #{loop_device}")
|
|
91
|
+
|
|
92
|
+
@log.trace "Offsets:\n#{offsets}"
|
|
93
|
+
|
|
94
|
+
offsets
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def create_disk(disk, size)
|
|
98
|
+
@log.trace "Preparing disk..."
|
|
99
|
+
@exec_helper.execute "dd if=/dev/zero of=#{disk} bs=1 count=0 seek=#{size * 1024}M"
|
|
100
|
+
@log.trace "Disk prepared"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def create_filesystem(disk, options = {})
|
|
104
|
+
options = {:type => 'ext3', :label => '/'}.merge(options)
|
|
105
|
+
|
|
106
|
+
@log.trace "Creating filesystem..."
|
|
107
|
+
@exec_helper.execute "mke2fs -T #{options[:type]} -L '#{options[:label]}' -F #{disk}"
|
|
108
|
+
@log.trace "Filesystem created"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def sync_files(from_dir, to_dir)
|
|
112
|
+
@log.debug "Syncing files between #{from_dir} and #{to_dir}..."
|
|
113
|
+
@exec_helper.execute "rsync -u -r -a #{from_dir}/* #{to_dir}"
|
|
114
|
+
@log.debug "Sync finished."
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def customize(disk_path)
|
|
118
|
+
GuestFSHelper.new(disk_path, :log => @log).customize do |guestfs, guestfs_helper|
|
|
119
|
+
yield guestfs, guestfs_helper
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -54,7 +54,7 @@ module BoxGrinder
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def read_and_require
|
|
57
|
-
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) + parse_plugin_list
|
|
57
|
+
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
|
|
58
58
|
|
|
59
59
|
plugins.flatten.each do |plugin|
|
|
60
60
|
@log.trace "Requiring plugin '#{plugin}'..."
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
|
20
20
|
|
|
21
21
|
require 'boxgrinder-core/helpers/exec-helper'
|
|
22
|
-
require 'boxgrinder-build/helpers/
|
|
22
|
+
require 'boxgrinder-build/helpers/image-helper'
|
|
23
23
|
require 'ostruct'
|
|
24
24
|
require 'openhash/openhash'
|
|
25
25
|
require 'fileutils'
|
|
@@ -34,6 +34,7 @@ module BoxGrinder
|
|
|
34
34
|
|
|
35
35
|
@log = options[:log] || Logger.new(STDOUT)
|
|
36
36
|
@exec_helper = options[:exec_helper] || ExecHelper.new( :log => @log )
|
|
37
|
+
@image_helper = options[:image_helper] || ImageHelper.new( :log => @log )
|
|
37
38
|
|
|
38
39
|
@plugin_info = options[:plugin_info]
|
|
39
40
|
@previous_plugin_info = options[:previous_plugin_info]
|
|
@@ -42,6 +43,7 @@ module BoxGrinder
|
|
|
42
43
|
@plugin_config = {}
|
|
43
44
|
|
|
44
45
|
@deliverables = OpenHash.new
|
|
46
|
+
@supported_oses = OpenHash.new
|
|
45
47
|
@target_deliverables = OpenHash.new
|
|
46
48
|
@dir = OpenHash.new
|
|
47
49
|
|
|
@@ -69,6 +71,29 @@ module BoxGrinder
|
|
|
69
71
|
end
|
|
70
72
|
end
|
|
71
73
|
|
|
74
|
+
def register_supported_os( name, versions )
|
|
75
|
+
raise "You can register supported operating system only after the plugin is initialized, please initialize the plugin using init method." if @initialized.nil?
|
|
76
|
+
|
|
77
|
+
@supported_oses[name] = OpenHash.new if @supported_oses[name].nil?
|
|
78
|
+
@supported_oses[name] = versions
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def is_supported_os?
|
|
82
|
+
return false unless !@supported_oses[@appliance_config.os.name].nil? and @supported_oses[@appliance_config.os.name].include?(@appliance_config.os.version)
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def supported_oses
|
|
87
|
+
supported = ""
|
|
88
|
+
|
|
89
|
+
@supported_oses.each do |name, versions|
|
|
90
|
+
supported << ", " unless supported.empty?
|
|
91
|
+
supported << "#{name} (versions: #{versions.join(", ")})"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
supported
|
|
95
|
+
end
|
|
96
|
+
|
|
72
97
|
def validate_plugin_config(fields = [], doc = nil)
|
|
73
98
|
more_info = doc.nil? ? '' : "See #{doc} for more info"
|
|
74
99
|
|
|
@@ -135,16 +160,7 @@ module BoxGrinder
|
|
|
135
160
|
begin
|
|
136
161
|
@plugin_config = YAML.load_file(@config_file)
|
|
137
162
|
rescue
|
|
138
|
-
raise "An error occurred while reading configuration file #{@config_file} for #{self.class.name}. Is it a valid YAML file?"
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# TODO consider removing this from BasePlugin and moving to another base class
|
|
143
|
-
def customize( disk_path )
|
|
144
|
-
raise "Customizing cannot be started until the plugin isn't initialized" if @initialized.nil?
|
|
145
|
-
|
|
146
|
-
GuestFSHelper.new( disk_path, :log => @log ).customize do |guestfs, guestfs_helper|
|
|
147
|
-
yield guestfs, guestfs_helper
|
|
163
|
+
raise "An error occurred while reading configuration file '#{@config_file}' for #{self.class.name}. Is it a valid YAML file?"
|
|
148
164
|
end
|
|
149
165
|
end
|
|
150
166
|
end
|
data/spec/appliance-spec.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'boxgrinder-build/appliance'
|
|
2
2
|
require 'rspec/rspec-config-helper'
|
|
3
|
-
require 'rbconfig'
|
|
4
3
|
require 'ostruct'
|
|
5
4
|
|
|
6
5
|
module BoxGrinder
|
|
@@ -8,188 +7,185 @@ module BoxGrinder
|
|
|
8
7
|
include RSpecConfigHelper
|
|
9
8
|
|
|
10
9
|
before(:all) do
|
|
11
|
-
@arch =
|
|
10
|
+
@arch = `uname -m`.chomp.strip
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
def prepare_appliance( options = OpenStruct.new )
|
|
15
14
|
options.name = 'boxgrinder'
|
|
16
15
|
options.version = '1.0'
|
|
17
16
|
|
|
18
|
-
@
|
|
17
|
+
@options = options
|
|
18
|
+
@log = Logger.new('/dev/null')
|
|
19
|
+
|
|
20
|
+
@plugin_manager = mock( PluginManager )
|
|
21
|
+
|
|
22
|
+
PluginManager.stub!(:instance).and_return( @plugin_manager )
|
|
23
|
+
|
|
24
|
+
@appliance = Appliance.new( "#{File.dirname( __FILE__ )}/rspec/src/appliances/jeos-f13.appl", :log => @log, :options => @options )
|
|
19
25
|
end
|
|
20
26
|
|
|
21
27
|
before(:each) do
|
|
22
28
|
prepare_appliance
|
|
23
29
|
end
|
|
24
30
|
|
|
25
|
-
it "should
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
ApplianceConfigValidator.should_receive(:new).with(any_args).and_return(validator)
|
|
30
|
-
|
|
31
|
-
plugin_manager_first_call = mock('PluginManagerOne')
|
|
32
|
-
plugin_manager_first_call.should_receive(:plugins).and_return({})
|
|
31
|
+
it "should prepare appliance to build" do
|
|
32
|
+
plugin_helper = mock(PluginHelper)
|
|
33
|
+
plugin_helper.should_receive(:load_plugins)
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
os_plugin = mock('OS Plugin')
|
|
37
|
-
os_plugin.should_receive(:init)
|
|
38
|
-
os_plugin.should_receive(:deliverables_exists?).and_return(false)
|
|
39
|
-
os_plugin.should_receive(:run)
|
|
40
|
-
os_plugin.should_receive(:deliverables).and_return({ :disk => 'abc'})
|
|
35
|
+
PluginHelper.should_receive( :new ).with( :options => @options, :log => @log ).and_return( plugin_helper )
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
PluginManager.should_receive(:instance).and_return(plugin_manager_second_call)
|
|
37
|
+
@appliance.should_receive(:read_and_validate_definition)
|
|
38
|
+
@appliance.should_not_receive(:remove_old_builds)
|
|
39
|
+
@appliance.should_receive(:execute_plugin_chain)
|
|
46
40
|
|
|
47
41
|
@appliance.create
|
|
48
42
|
end
|
|
49
43
|
|
|
50
|
-
it "should
|
|
51
|
-
prepare_appliance( OpenStruct.new(
|
|
52
|
-
|
|
53
|
-
validator = mock(ApplianceConfigValidator)
|
|
54
|
-
validator.should_receive(:validate)
|
|
55
|
-
|
|
56
|
-
ApplianceConfigValidator.should_receive(:new).with(any_args).and_return(validator)
|
|
57
|
-
|
|
58
|
-
os_plugin_output = {}
|
|
59
|
-
|
|
60
|
-
@appliance.should_receive(:execute_os_plugin).and_return(os_plugin_output)
|
|
61
|
-
|
|
62
|
-
plugin_manager_first_call = mock('PluginManagerOne')
|
|
63
|
-
plugin_manager_first_call.should_receive(:plugins).and_return({})
|
|
64
|
-
|
|
65
|
-
PluginManager.should_receive(:instance).and_return(plugin_manager_first_call)
|
|
44
|
+
it "should prepare appliance to build with removing old files" do
|
|
45
|
+
prepare_appliance( OpenStruct.new( :force => true ) )
|
|
66
46
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
platform_plugin.should_receive(:deliverables_exists?).and_return(false)
|
|
70
|
-
platform_plugin.should_receive(:run)
|
|
71
|
-
platform_plugin.should_receive(:deliverables).and_return({ :disk => 'abc'})
|
|
47
|
+
plugin_helper = mock(PluginHelper)
|
|
48
|
+
plugin_helper.should_receive(:load_plugins)
|
|
72
49
|
|
|
73
|
-
|
|
74
|
-
plugin_manager_second_call.should_receive(:initialize_plugin).with(:platform, :vmware).and_return([ platform_plugin, {:class => Appliance, :type => :platform, :name => :vmware, :full_name => "VMware"} ] )
|
|
50
|
+
PluginHelper.should_receive( :new ).with( :options => @options, :log => @log ).and_return( plugin_helper )
|
|
75
51
|
|
|
76
|
-
|
|
52
|
+
@appliance.should_receive(:read_and_validate_definition)
|
|
53
|
+
@appliance.should_receive(:remove_old_builds)
|
|
54
|
+
@appliance.should_receive(:execute_plugin_chain)
|
|
77
55
|
|
|
78
56
|
@appliance.create
|
|
79
57
|
end
|
|
80
58
|
|
|
81
|
-
it "should
|
|
82
|
-
|
|
59
|
+
it "should read and validate definition" do
|
|
60
|
+
appliance_config = ApplianceConfig.new
|
|
83
61
|
|
|
84
|
-
|
|
85
|
-
|
|
62
|
+
appliance_helper = mock(ApplianceHelper)
|
|
63
|
+
appliance_helper.should_receive(:read_definitions).with( "#{File.dirname( __FILE__ )}/rspec/src/appliances/jeos-f13.appl" ).and_return([{}, appliance_config])
|
|
86
64
|
|
|
87
|
-
|
|
65
|
+
ApplianceHelper.should_receive(:new).with( :log => @log ).and_return(appliance_helper)
|
|
88
66
|
|
|
89
|
-
|
|
90
|
-
platform_plugin_output = { :abc => 'def'}
|
|
67
|
+
appliance_config_helper = mock(ApplianceConfigHelper)
|
|
91
68
|
|
|
92
|
-
|
|
93
|
-
|
|
69
|
+
appliance_config.should_receive(:clone).and_return(appliance_config)
|
|
70
|
+
appliance_config.should_receive(:init_arch).and_return(appliance_config)
|
|
71
|
+
appliance_config.should_receive(:initialize_paths).and_return(appliance_config)
|
|
94
72
|
|
|
95
|
-
|
|
96
|
-
plugin_manager_first_call.should_receive(:plugins).and_return({})
|
|
73
|
+
appliance_config_helper.should_receive(:merge).with( appliance_config ).and_return( appliance_config )
|
|
97
74
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
delivery_plugin = mock('S3 Plugin')
|
|
101
|
-
delivery_plugin.should_receive(:init)
|
|
102
|
-
delivery_plugin.should_receive(:run).with(:ami)
|
|
75
|
+
ApplianceConfigHelper.should_receive(:new).with( {} ).and_return( appliance_config_helper )
|
|
103
76
|
|
|
104
|
-
|
|
105
|
-
|
|
77
|
+
appliance_config_validator = mock(ApplianceConfigValidator)
|
|
78
|
+
appliance_config_validator.should_receive(:validate)
|
|
106
79
|
|
|
107
|
-
|
|
80
|
+
ApplianceConfigValidator.should_receive(:new).with( appliance_config ).and_return(appliance_config_validator)
|
|
108
81
|
|
|
109
|
-
@appliance.
|
|
82
|
+
@appliance.read_and_validate_definition
|
|
110
83
|
end
|
|
111
|
-
it "should build appliance, convert it to vmware format and deliver it using sftp ami type" do
|
|
112
|
-
prepare_appliance( OpenStruct.new({ :platform => :vmware, :delivery => :sftp }) )
|
|
113
84
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
85
|
+
it "should remove old builds" do
|
|
86
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
87
|
+
FileUtils.should_receive(:rm_rf).with("build/appliances/#{@arch}/fedora/11/full")
|
|
88
|
+
@appliance.remove_old_builds
|
|
89
|
+
end
|
|
118
90
|
|
|
119
|
-
|
|
120
|
-
|
|
91
|
+
it "should build base appliance" do
|
|
92
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
121
93
|
|
|
122
|
-
|
|
123
|
-
|
|
94
|
+
os_plugin = mock('FedoraPlugin')
|
|
95
|
+
os_plugin.should_receive(:init)
|
|
96
|
+
os_plugin.should_receive(:deliverables_exists?).and_return(false)
|
|
97
|
+
os_plugin.should_receive(:run)
|
|
98
|
+
os_plugin.should_receive(:deliverables).and_return({ :disk => 'abc'})
|
|
124
99
|
|
|
125
|
-
|
|
126
|
-
|
|
100
|
+
@plugin_manager.should_receive(:plugins).and_return( { :os => "something" } )
|
|
101
|
+
@plugin_manager.should_receive(:initialize_plugin).once.with(:os, :fedora).and_return([ os_plugin, {:class => Appliance, :type => :os, :name => :fedora, :full_name => "Fedora", :versions => ["11", "12", "13", "rawhide"] } ] )
|
|
127
102
|
|
|
128
|
-
|
|
103
|
+
@appliance.execute_plugin_chain
|
|
104
|
+
end
|
|
129
105
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
delivery_plugin.should_receive(:run).with(no_args)
|
|
106
|
+
it "should not build base appliance because deliverable already exists" do
|
|
107
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
133
108
|
|
|
134
|
-
|
|
135
|
-
|
|
109
|
+
os_plugin = mock('FedoraPlugin')
|
|
110
|
+
os_plugin.should_receive(:init)
|
|
111
|
+
os_plugin.should_receive(:deliverables_exists?).and_return(true)
|
|
112
|
+
os_plugin.should_not_receive(:run)
|
|
113
|
+
os_plugin.should_receive(:deliverables).and_return({ :disk => 'abc'})
|
|
136
114
|
|
|
137
|
-
|
|
115
|
+
@plugin_manager.should_receive(:plugins).and_return( { :os => "something" } )
|
|
116
|
+
@plugin_manager.should_receive(:initialize_plugin).once.with(:os, :fedora).and_return([ os_plugin, {:class => Appliance, :type => :os, :name => :fedora, :full_name => "Fedora", :versions => ["11", "12", "13", "rawhide"] } ] )
|
|
138
117
|
|
|
139
|
-
@appliance.
|
|
118
|
+
@appliance.execute_plugin_chain
|
|
140
119
|
end
|
|
141
120
|
|
|
142
|
-
it "should
|
|
143
|
-
prepare_appliance( OpenStruct.new( :
|
|
144
|
-
|
|
145
|
-
validator = mock(ApplianceConfigValidator)
|
|
146
|
-
validator.should_receive(:validate)
|
|
121
|
+
it "should build appliance and convert it to VMware format" do
|
|
122
|
+
prepare_appliance( OpenStruct.new({ :platform => :vmware }) )
|
|
147
123
|
|
|
148
|
-
|
|
124
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
125
|
+
@appliance.should_receive( :execute_os_plugin ).and_return( {} )
|
|
149
126
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
127
|
+
platform_plugin = mock('VMware Plugin')
|
|
128
|
+
platform_plugin.should_receive(:init)
|
|
129
|
+
platform_plugin.should_receive(:deliverables_exists?).and_return(false)
|
|
130
|
+
platform_plugin.should_receive(:run)
|
|
131
|
+
platform_plugin.should_receive(:deliverables).and_return({ :disk => 'abc' })
|
|
153
132
|
|
|
154
|
-
|
|
133
|
+
@plugin_manager.should_receive(:plugins).and_return( { :platform => "something" } )
|
|
134
|
+
@plugin_manager.should_receive(:initialize_plugin).once.with(:platform, :vmware).and_return([ platform_plugin, {:class => Appliance, :type => :platform, :name => :vmware, :full_name => "VMware"} ] )
|
|
155
135
|
|
|
156
|
-
@appliance.
|
|
136
|
+
@appliance.execute_plugin_chain
|
|
157
137
|
end
|
|
158
138
|
|
|
159
|
-
it "should
|
|
139
|
+
it "should build appliance and convert it to VMware format because deliverable already exists" do
|
|
160
140
|
prepare_appliance( OpenStruct.new({ :platform => :vmware }) )
|
|
161
141
|
|
|
162
|
-
|
|
163
|
-
|
|
142
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
143
|
+
@appliance.should_receive( :execute_os_plugin ).and_return( {} )
|
|
164
144
|
|
|
165
|
-
|
|
145
|
+
platform_plugin = mock('VMware Plugin')
|
|
146
|
+
platform_plugin.should_receive(:init)
|
|
147
|
+
platform_plugin.should_receive(:deliverables_exists?).and_return(true)
|
|
148
|
+
platform_plugin.should_not_receive(:run)
|
|
149
|
+
platform_plugin.should_receive(:deliverables).and_return({ :disk => 'abc' })
|
|
166
150
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
151
|
+
@plugin_manager.should_receive(:plugins).and_return( { :platform => "something" } )
|
|
152
|
+
@plugin_manager.should_receive(:initialize_plugin).once.with(:platform, :vmware).and_return([ platform_plugin, {:class => Appliance, :type => :platform, :name => :vmware, :full_name => "VMware"} ] )
|
|
153
|
+
|
|
154
|
+
@appliance.execute_plugin_chain
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "should build appliance, convert it to EC2 format and deliver it using S3 ami type" do
|
|
158
|
+
prepare_appliance( OpenStruct.new({ :platform => :ec2, :delivery => :ami }) )
|
|
159
|
+
|
|
160
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
161
|
+
@appliance.should_receive( :execute_os_plugin ).and_return( { :abc => 'def'} )
|
|
162
|
+
@appliance.should_receive( :execute_platform_plugin ).with( { :abc => 'def'} ).and_return( { :def => 'ghi'} )
|
|
171
163
|
|
|
172
|
-
|
|
173
|
-
|
|
164
|
+
delivery_plugin = mock('S3 Plugin')
|
|
165
|
+
delivery_plugin.should_receive(:init)
|
|
166
|
+
delivery_plugin.should_receive(:run).with(:ami)
|
|
174
167
|
|
|
175
|
-
|
|
168
|
+
@plugin_manager.should_receive(:plugins).and_return( { :delivery => "something" } )
|
|
169
|
+
@plugin_manager.should_receive(:initialize_plugin).with(:delivery, :ami).and_return([ delivery_plugin, {:class => Appliance, :type => :delivery, :name => :s3, :full_name => "Amazon Simple Storage Service (Amazon S3)", :types => [:s3, :cloudfront, :ami]} ] )
|
|
176
170
|
|
|
177
|
-
|
|
178
|
-
|
|
171
|
+
@appliance.execute_plugin_chain
|
|
172
|
+
end
|
|
179
173
|
|
|
180
|
-
|
|
174
|
+
it "should build appliance, convert it to EC2 format and deliver it using delivery plugin with only one delivery type" do
|
|
175
|
+
prepare_appliance( OpenStruct.new({ :platform => :ec2, :delivery => :same }) )
|
|
181
176
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
platform_plugin.should_receive(:deliverables).and_return({ :disk => 'def'})
|
|
177
|
+
@appliance.instance_variable_set(:@appliance_config, generate_appliance_config )
|
|
178
|
+
@appliance.should_receive( :execute_os_plugin ).and_return( { :abc => 'def'} )
|
|
179
|
+
@appliance.should_receive( :execute_platform_plugin ).with( { :abc => 'def'} ).and_return( { :def => 'ghi'} )
|
|
186
180
|
|
|
187
|
-
|
|
188
|
-
|
|
181
|
+
delivery_plugin = mock('S3 Plugin')
|
|
182
|
+
delivery_plugin.should_receive(:init)
|
|
183
|
+
delivery_plugin.should_receive(:run).with(no_args())
|
|
189
184
|
|
|
190
|
-
|
|
185
|
+
@plugin_manager.should_receive(:plugins).and_return( { :delivery => "something" } )
|
|
186
|
+
@plugin_manager.should_receive(:initialize_plugin).with(:delivery, :same).and_return([ delivery_plugin, {:class => Appliance, :type => :delivery, :name => :same, :full_name => "A plugin"} ] )
|
|
191
187
|
|
|
192
|
-
@appliance.
|
|
188
|
+
@appliance.execute_plugin_chain
|
|
193
189
|
end
|
|
194
190
|
end
|
|
195
191
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2010 Red Hat, Inc.
|
|
3
|
+
#
|
|
4
|
+
# This is free software; you can redistribute it and/or modify it
|
|
5
|
+
# under the terms of the GNU Lesser General Public License as
|
|
6
|
+
# published by the Free Software Foundation; either version 3 of
|
|
7
|
+
# the License, or (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This software is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+
# Lesser General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
|
15
|
+
# License along with this software; if not, write to the Free
|
|
16
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
17
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
|
18
|
+
|
|
19
|
+
require 'boxgrinder-build/helpers/image-helper'
|
|
20
|
+
|
|
21
|
+
module BoxGrinder
|
|
22
|
+
describe ImageHelper do
|
|
23
|
+
|
|
24
|
+
before(:each) do
|
|
25
|
+
@helper = ImageHelper.new(:log => Logger.new('/dev/null'))
|
|
26
|
+
|
|
27
|
+
@log = @helper.instance_variable_get(:@log)
|
|
28
|
+
@exec_helper = @helper.instance_variable_get(:@exec_helper)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should mount image with one root partition" do
|
|
32
|
+
@helper.should_receive(:calculate_disk_offsets).with('disk.raw').and_return(['0'])
|
|
33
|
+
FileUtils.should_receive(:mkdir_p).with('mount_dir')
|
|
34
|
+
@helper.should_receive(:get_loop_device).and_return('/dev/loop0')
|
|
35
|
+
@exec_helper.should_receive(:execute).with('losetup -o 0 /dev/loop0 disk.raw')
|
|
36
|
+
@exec_helper.should_receive(:execute).with('e2label /dev/loop0').and_return('/')
|
|
37
|
+
@exec_helper.should_receive(:execute).with("df -T /dev/loop0 | tail -1 | awk '{print $2}'").and_return('ext3')
|
|
38
|
+
@exec_helper.should_receive(:execute).with('mount /dev/loop0 -t ext3 mount_dir')
|
|
39
|
+
|
|
40
|
+
@helper.mount_image('disk.raw', 'mount_dir').should == {"/"=>"/dev/loop0"}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should mount image with two partitions with support for new livecd-tools partitions labels starting with '_'" do
|
|
44
|
+
@helper.should_receive(:calculate_disk_offsets).with('disk.raw').and_return(['322', '562'])
|
|
45
|
+
FileUtils.should_receive(:mkdir_p).with('mount_dir')
|
|
46
|
+
@helper.should_receive(:get_loop_device).and_return('/dev/loop0')
|
|
47
|
+
@exec_helper.should_receive(:execute).with('losetup -o 322 /dev/loop0 disk.raw')
|
|
48
|
+
@exec_helper.should_receive(:execute).with('e2label /dev/loop0').and_return('_/home')
|
|
49
|
+
@helper.should_receive(:get_loop_device).and_return('/dev/loop1')
|
|
50
|
+
@exec_helper.should_receive(:execute).with('losetup -o 562 /dev/loop1 disk.raw')
|
|
51
|
+
@exec_helper.should_receive(:execute).with('e2label /dev/loop1').and_return('_/')
|
|
52
|
+
|
|
53
|
+
@exec_helper.should_receive(:execute).with("df -T /dev/loop1 | tail -1 | awk '{print $2}'").and_return('ext3')
|
|
54
|
+
@exec_helper.should_receive(:execute).with("df -T /dev/loop0 | tail -1 | awk '{print $2}'").and_return('ext4')
|
|
55
|
+
|
|
56
|
+
@exec_helper.should_receive(:execute).with('mount /dev/loop1 -t ext3 mount_dir')
|
|
57
|
+
@exec_helper.should_receive(:execute).with('mount /dev/loop0 -t ext4 mount_dir/home')
|
|
58
|
+
|
|
59
|
+
@helper.mount_image('disk.raw', 'mount_dir').should == {"/"=>"/dev/loop1", "/home"=>"/dev/loop0"}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should umount the image" do
|
|
63
|
+
@exec_helper.should_receive(:execute).ordered.with('umount -d /dev/loop0')
|
|
64
|
+
@exec_helper.should_receive(:execute).ordered.with('umount -d /dev/loop1')
|
|
65
|
+
FileUtils.should_receive(:rm_rf).with('mount_dir')
|
|
66
|
+
|
|
67
|
+
@helper.umount_image('disk.raw', 'mount_dir', {"/"=>"/dev/loop1", "/home"=>"/dev/loop0"})
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should get free loop device" do
|
|
71
|
+
@exec_helper.should_receive(:execute).with('losetup -f 2>&1').and_return("/dev/loop0\n")
|
|
72
|
+
|
|
73
|
+
@helper.get_loop_device.should == '/dev/loop0'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "shouldn't get free loop device because there are no free loop devices left" do
|
|
77
|
+
@exec_helper.should_receive(:execute).with('losetup -f 2>&1').and_raise('boom')
|
|
78
|
+
|
|
79
|
+
begin
|
|
80
|
+
@helper.get_loop_device
|
|
81
|
+
raise "Shouldn't raise"
|
|
82
|
+
rescue => e
|
|
83
|
+
e.message.should == "No free loop devices available, please free at least one. See 'losetup -d' command."
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should calculate disks offsets" do
|
|
88
|
+
@helper.should_receive(:get_loop_device).and_return('/dev/loop0')
|
|
89
|
+
@exec_helper.should_receive(:execute).ordered.with('losetup /dev/loop0 disk.raw')
|
|
90
|
+
@exec_helper.should_receive(:execute).ordered.with("parted /dev/loop0 'unit B print' | grep -e '^ [0-9]' | awk '{ print $2 }'").and_return("0B\n1234B\n")
|
|
91
|
+
@exec_helper.should_receive(:execute).ordered.with('losetup -d /dev/loop0')
|
|
92
|
+
|
|
93
|
+
@helper.calculate_disk_offsets('disk.raw').should == ["0", "1234"]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should create a new empty disk image" do
|
|
97
|
+
@exec_helper.should_receive(:execute).with('dd if=/dev/zero of=disk.raw bs=1 count=0 seek=10240M')
|
|
98
|
+
|
|
99
|
+
@helper.create_disk('disk.raw', 10)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should create default filesystem on selected device" do
|
|
103
|
+
@exec_helper.should_receive(:execute).with("mke2fs -T ext3 -L '/' -F /dev/loop0")
|
|
104
|
+
|
|
105
|
+
@helper.create_filesystem('/dev/loop0')
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "should create ext4 filesystem on selected device" do
|
|
109
|
+
@exec_helper.should_receive(:execute).with("mke2fs -T ext4 -L '/' -F /dev/loop0")
|
|
110
|
+
|
|
111
|
+
@helper.create_filesystem('/dev/loop0', :type => 'ext4')
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "should create ext4 filesystem on selected device with a label" do
|
|
115
|
+
@exec_helper.should_receive(:execute).with("mke2fs -T ext4 -L '/home' -F /dev/loop0")
|
|
116
|
+
|
|
117
|
+
@helper.create_filesystem('/dev/loop0', :type => 'ext4', :label => '/home')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
it "should sync files" do
|
|
122
|
+
@exec_helper.should_receive(:execute).with("rsync -u -r -a from_dir/* to_dir")
|
|
123
|
+
|
|
124
|
+
@helper.sync_files('from_dir', 'to_dir')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "should customize the disk image suing GuestFS" do
|
|
128
|
+
guestfs = mock('GuestFS')
|
|
129
|
+
guestfs.should_receive(:abc)
|
|
130
|
+
|
|
131
|
+
guestfs_helper = mock(GuestFSHelper)
|
|
132
|
+
guestfs_helper.should_receive(:customize).ordered.and_yield(guestfs, guestfs_helper)
|
|
133
|
+
guestfs_helper.should_receive(:def)
|
|
134
|
+
|
|
135
|
+
GuestFSHelper.should_receive(:new).with('disk.raw', :log => @log).and_return(guestfs_helper)
|
|
136
|
+
|
|
137
|
+
@helper.customize('disk.raw') do |guestfs, guestfs_helper|
|
|
138
|
+
guestfs.abc
|
|
139
|
+
guestfs_helper.def
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
@@ -8,7 +8,7 @@ module BoxGrinder
|
|
|
8
8
|
|
|
9
9
|
before(:all) do
|
|
10
10
|
@current_arch = (-1.size) == 8 ? "x86_64" : "i386"
|
|
11
|
-
@plugin_array = %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)
|
|
11
|
+
@plugin_array = %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)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
before(:each) do
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
require 'boxgrinder-build/plugins/base-plugin'
|
|
2
2
|
require 'rspec/rspec-config-helper'
|
|
3
|
-
require 'rbconfig'
|
|
4
3
|
|
|
5
4
|
module BoxGrinder
|
|
6
5
|
describe BasePlugin do
|
|
7
6
|
include RSpecConfigHelper
|
|
8
7
|
|
|
9
8
|
before(:all) do
|
|
10
|
-
@arch =
|
|
9
|
+
@arch = `uname -m`.chomp.strip
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
before(:each) do
|
|
14
13
|
@plugin = BasePlugin.new
|
|
15
|
-
@plugin.init(
|
|
14
|
+
@plugin.init(generate_config, generate_appliance_config, :plugin_info => {:name => :plugin_name}, :log => Logger.new('/dev/null'))
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
it "should be initialized after running init method" do
|
|
@@ -78,12 +77,78 @@ module BoxGrinder
|
|
|
78
77
|
FileUtils.should_receive(:rm_rf).with("build/appliances/#{@arch}/fedora/11/full/plugin_name-plugin/tmp")
|
|
79
78
|
FileUtils.should_receive(:mkdir_p).with("build/appliances/#{@arch}/fedora/11/full/plugin_name-plugin/tmp")
|
|
80
79
|
|
|
81
|
-
@plugin.should_receive(
|
|
80
|
+
@plugin.should_receive(:execute).with('a', 3)
|
|
82
81
|
|
|
83
82
|
FileUtils.should_receive(:mv).with("build/appliances/#{@arch}/fedora/11/full/plugin_name-plugin/tmp/disk", "build/appliances/#{@arch}/fedora/11/full/plugin_name-plugin/disk")
|
|
84
83
|
FileUtils.should_receive(:rm_rf).with("build/appliances/#{@arch}/fedora/11/full/plugin_name-plugin/tmp")
|
|
85
84
|
|
|
86
85
|
@plugin.run('a', 3)
|
|
87
86
|
end
|
|
87
|
+
|
|
88
|
+
it "should register a supported os" do
|
|
89
|
+
@plugin.register_supported_os('fedora', ['12', '13'])
|
|
90
|
+
|
|
91
|
+
oses = @plugin.instance_variable_get(:@supported_oses)
|
|
92
|
+
|
|
93
|
+
oses.size.should == 1
|
|
94
|
+
oses['fedora'].size.should == 2
|
|
95
|
+
oses['fedora'].should == ['12', '13']
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should return that the OS is supported" do
|
|
99
|
+
@plugin.register_supported_os('fedora', ['12', '13'])
|
|
100
|
+
|
|
101
|
+
@plugin.instance_variable_get(:@appliance_config).os.name = 'fedora'
|
|
102
|
+
@plugin.instance_variable_get(:@appliance_config).os.version = '12'
|
|
103
|
+
|
|
104
|
+
@plugin.is_supported_os?.should == true
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should return that the OS is not supported" do
|
|
108
|
+
@plugin.register_supported_os('fedora', ['1223'])
|
|
109
|
+
|
|
110
|
+
@plugin.instance_variable_get(:@appliance_config).os.name = 'fedora'
|
|
111
|
+
@plugin.instance_variable_get(:@appliance_config).os.version = '12'
|
|
112
|
+
|
|
113
|
+
@plugin.is_supported_os?.should == false
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "should return supported os string" do
|
|
117
|
+
@plugin.register_supported_os('fedora', ['12', '13'])
|
|
118
|
+
@plugin.register_supported_os('centos', ['5'])
|
|
119
|
+
|
|
120
|
+
@plugin.supported_oses.should == "fedora (versions: 12, 13), centos (versions: 5)"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should set default config value" do
|
|
124
|
+
@plugin.set_default_config_value('key', 'avalue')
|
|
125
|
+
|
|
126
|
+
@plugin.instance_variable_get(:@plugin_config)['key'].should == 'avalue'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should read plugin config" do
|
|
130
|
+
@plugin.instance_variable_set(:@config_file, "configfile")
|
|
131
|
+
|
|
132
|
+
File.should_receive(:exists?).with('configfile').and_return(true)
|
|
133
|
+
YAML.should_receive(:load_file).with('configfile').and_return('abcdef')
|
|
134
|
+
|
|
135
|
+
@plugin.read_plugin_config
|
|
136
|
+
|
|
137
|
+
@plugin.instance_variable_get(:@plugin_config).should == 'abcdef'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should read plugin config and raise an exception" do
|
|
141
|
+
@plugin.instance_variable_set(:@config_file, "configfile")
|
|
142
|
+
|
|
143
|
+
File.should_receive(:exists?).with('configfile').and_return(true)
|
|
144
|
+
YAML.should_receive(:load_file).with('configfile').and_raise('something')
|
|
145
|
+
|
|
146
|
+
begin
|
|
147
|
+
@plugin.read_plugin_config
|
|
148
|
+
raise "Should raise"
|
|
149
|
+
rescue => e
|
|
150
|
+
e.message.should == "An error occurred while reading configuration file 'configfile' for BoxGrinder::BasePlugin. Is it a valid YAML file?"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
88
153
|
end
|
|
89
154
|
end
|
metadata
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: boxgrinder-build
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 7
|
|
4
5
|
prerelease: false
|
|
5
6
|
segments:
|
|
6
7
|
- 0
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
version: 0.
|
|
8
|
+
- 6
|
|
9
|
+
- 0
|
|
10
|
+
version: 0.6.0
|
|
10
11
|
platform: ruby
|
|
11
12
|
authors:
|
|
12
13
|
- Marek Goldmann
|
|
@@ -14,16 +15,18 @@ autorequire:
|
|
|
14
15
|
bindir: bin
|
|
15
16
|
cert_chain: []
|
|
16
17
|
|
|
17
|
-
date: 2010-
|
|
18
|
+
date: 2010-10-12 00:00:00 +02:00
|
|
18
19
|
default_executable:
|
|
19
20
|
dependencies:
|
|
20
21
|
- !ruby/object:Gem::Dependency
|
|
21
22
|
name: commander
|
|
22
23
|
prerelease: false
|
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
24
26
|
requirements:
|
|
25
27
|
- - ~>
|
|
26
28
|
- !ruby/object:Gem::Version
|
|
29
|
+
hash: 57
|
|
27
30
|
segments:
|
|
28
31
|
- 4
|
|
29
32
|
- 0
|
|
@@ -35,14 +38,16 @@ dependencies:
|
|
|
35
38
|
name: boxgrinder-core
|
|
36
39
|
prerelease: false
|
|
37
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
38
42
|
requirements:
|
|
39
43
|
- - ~>
|
|
40
44
|
- !ruby/object:Gem::Version
|
|
45
|
+
hash: 27
|
|
41
46
|
segments:
|
|
42
47
|
- 0
|
|
48
|
+
- 1
|
|
43
49
|
- 0
|
|
44
|
-
|
|
45
|
-
version: 0.0.22
|
|
50
|
+
version: 0.1.0
|
|
46
51
|
type: :runtime
|
|
47
52
|
version_requirements: *id002
|
|
48
53
|
description: A tool for creating appliances from simple plain text files for various virtual environments.
|
|
@@ -60,6 +65,7 @@ extra_rdoc_files:
|
|
|
60
65
|
- lib/boxgrinder-build/helpers/appliance-customize-helper.rb
|
|
61
66
|
- lib/boxgrinder-build/helpers/augeas-helper.rb
|
|
62
67
|
- lib/boxgrinder-build/helpers/guestfs-helper.rb
|
|
68
|
+
- lib/boxgrinder-build/helpers/image-helper.rb
|
|
63
69
|
- lib/boxgrinder-build/helpers/linux-helper.rb
|
|
64
70
|
- lib/boxgrinder-build/helpers/package-helper.rb
|
|
65
71
|
- lib/boxgrinder-build/helpers/plugin-helper.rb
|
|
@@ -76,6 +82,7 @@ files:
|
|
|
76
82
|
- lib/boxgrinder-build/helpers/appliance-customize-helper.rb
|
|
77
83
|
- lib/boxgrinder-build/helpers/augeas-helper.rb
|
|
78
84
|
- lib/boxgrinder-build/helpers/guestfs-helper.rb
|
|
85
|
+
- lib/boxgrinder-build/helpers/image-helper.rb
|
|
79
86
|
- lib/boxgrinder-build/helpers/linux-helper.rb
|
|
80
87
|
- lib/boxgrinder-build/helpers/package-helper.rb
|
|
81
88
|
- lib/boxgrinder-build/helpers/plugin-helper.rb
|
|
@@ -86,6 +93,7 @@ files:
|
|
|
86
93
|
- spec/helpers/appliance-customize-helper-spec.rb
|
|
87
94
|
- spec/helpers/augeas-helper-spec.rb
|
|
88
95
|
- spec/helpers/guestfs-helper-spec.rb
|
|
96
|
+
- spec/helpers/image-helper-spec.rb
|
|
89
97
|
- spec/helpers/linux-helper-spec.rb
|
|
90
98
|
- spec/helpers/package-helper-spec.rb
|
|
91
99
|
- spec/helpers/plugin-helper-spec.rb
|
|
@@ -94,7 +102,7 @@ files:
|
|
|
94
102
|
- spec/rspec/src/appliances/jeos-f13.appl
|
|
95
103
|
- boxgrinder-build.gemspec
|
|
96
104
|
has_rdoc: true
|
|
97
|
-
homepage: http://www.jboss.org/
|
|
105
|
+
homepage: http://www.jboss.org/boxgrinder
|
|
98
106
|
licenses: []
|
|
99
107
|
|
|
100
108
|
post_install_message:
|
|
@@ -108,16 +116,20 @@ rdoc_options:
|
|
|
108
116
|
require_paths:
|
|
109
117
|
- lib
|
|
110
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
|
+
none: false
|
|
111
120
|
requirements:
|
|
112
121
|
- - ">="
|
|
113
122
|
- !ruby/object:Gem::Version
|
|
123
|
+
hash: 3
|
|
114
124
|
segments:
|
|
115
125
|
- 0
|
|
116
126
|
version: "0"
|
|
117
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
|
+
none: false
|
|
118
129
|
requirements:
|
|
119
130
|
- - ">="
|
|
120
131
|
- !ruby/object:Gem::Version
|
|
132
|
+
hash: 11
|
|
121
133
|
segments:
|
|
122
134
|
- 1
|
|
123
135
|
- 2
|
|
@@ -125,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
137
|
requirements: []
|
|
126
138
|
|
|
127
139
|
rubyforge_project: BoxGrinder Build
|
|
128
|
-
rubygems_version: 1.3.
|
|
140
|
+
rubygems_version: 1.3.7
|
|
129
141
|
signing_key:
|
|
130
142
|
specification_version: 3
|
|
131
143
|
summary: A tool for creating appliances from simple plain text files for various virtual environments.
|