albanpeignier-system-builder 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,9 @@
1
+ === 0.0.2 2009-09-02
2
+
3
+ * 1 major enhancement:
4
+ * Add rake task
5
+
6
+ === 0.0.1 2009-09-01
7
+
8
+ * 1 major enhancement:
9
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,31 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/system-builder
7
+ examples/Rakefile
8
+ examples/config.rb
9
+ examples/manifests/classes/test.pp
10
+ examples/manifests/site.pp
11
+ features/development.feature
12
+ features/step_definitions/common_steps.rb
13
+ features/support/common.rb
14
+ features/support/env.rb
15
+ features/support/matchers.rb
16
+ lib/system_builder.rb
17
+ lib/system_builder/boot.rb
18
+ lib/system_builder/cli.rb
19
+ lib/system_builder/configurator.rb
20
+ lib/system_builder/core_ext.rb
21
+ lib/system_builder/image.rb
22
+ lib/system_builder/task.rb
23
+ script/console
24
+ script/destroy
25
+ script/generate
26
+ spec/spec.opts
27
+ spec/spec_helper.rb
28
+ spec/system-builder_cli_spec.rb
29
+ spec/system-builder_spec.rb
30
+ system-builder.gemspec
31
+ tasks/rspec.rake
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on system-builder, see http://system-builder.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = system-builder
2
+
3
+ * http://github.com/#{github_username}/#{project_name}
4
+
5
+ == DESCRIPTION:
6
+
7
+ FIX (describe your package)
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * FIX (list of features or problems)
12
+
13
+ == SYNOPSIS:
14
+
15
+ FIX (code sample of usage)
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * FIX (list of requirements)
20
+
21
+ == INSTALL:
22
+
23
+ * FIX (sudo gem install, anything else)
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2009 Alban Peignier
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/system_builder'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'system-builder' do
14
+ self.version = SystemBuilder::VERSION
15
+ self.developer 'Alban Peignier', 'alban.peignier@free.fr'
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ # self.extra_deps = [['activesupport','>= 2.0.2']]
18
+ end
19
+
20
+ require 'newgem/tasks'
21
+ Dir['tasks/**/*.rake'].each { |t| load t }
22
+
23
+ # TODO - want other tests/tasks run by default? Add them to the list
24
+ # remove_task :default
25
+ # task :default => [:spec, :features]
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Alban Peignier on 2009-9-1.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'rubygems'
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/system_builder")
8
+ require "system_builder/cli"
9
+
10
+ SystemBuilder::CLI.execute(STDOUT, ARGV)
data/examples/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'system_builder'
2
+ require 'system_builder/task'
3
+
4
+ def working_dir(name, type)
5
+ "/var/tmp/system-builder/#{name}/#{type}"
6
+ end
7
+
8
+ SystemBuilder::Task.new(:simple_test) do
9
+ SystemBuilder::DiskImage.new(working_dir("simple_test", :disk)).tap do |image|
10
+ image.boot = SystemBuilder::DebianBoot.new(working_dir("simple_test", :boot)).tap do |boot|
11
+ boot.mirror = "http://127.0.0.1:9999/debian"
12
+ end
13
+
14
+ image.boot.configure do |chroot|
15
+ chroot.apt_install :sudo
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ def working_dir(name, type)
2
+ "/var/tmp/system-builder/#{name}/#{type}"
3
+ end
4
+
5
+ SystemBuilder.config(:simple_test) do
6
+ SystemBuilder::DiskImage.new(working_dir("simple_test", :disk)).tap do |image|
7
+ image.boot = SystemBuilder::DebianBoot.new(working_dir("simple_test", :boot)).tap do |boot|
8
+ boot.mirror = "http://127.0.0.1:9999/debian"
9
+ end
10
+
11
+ image.boot.configure do |chroot|
12
+ chroot.apt_install :sudo
13
+ end
14
+ end
15
+ end
16
+
17
+ SystemBuilder.config(:puppet_test) do
18
+ SystemBuilder::DiskImage.new(working_dir("puppet_test", :disk)).tap do |image|
19
+ image.boot = SystemBuilder::DebianBoot.new(working_dir("puppet_test", :boot)).tap do |boot|
20
+ boot.mirror = "http://127.0.0.1:9999/debian"
21
+ end
22
+
23
+ # use manifests/site.pp in this directory
24
+ image.boot.configurators << SystemBuilder::PuppetConfigurator.new(File.dirname(__FILE__))
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ notice("test")
@@ -0,0 +1,3 @@
1
+ notice("puppet site configuration")
2
+
3
+ import "classes/*"
@@ -0,0 +1,13 @@
1
+ Feature: Development processes of newgem itself (rake tasks)
2
+
3
+ As a Newgem maintainer or contributor
4
+ I want rake tasks to maintain and release the gem
5
+ So that I can spend time on the tests and code, and not excessive time on maintenance processes
6
+
7
+ Scenario: Generate RubyGem
8
+ Given this project is active project folder
9
+ And "pkg" folder is deleted
10
+ When I invoke task "rake gem"
11
+ Then folder "pkg" is created
12
+ And file with name matching "pkg/*.gem" is created else you should run "rake manifest" to fix this
13
+ And gem spec key "rdoc_options" contains /--mainREADME.rdoc/
@@ -0,0 +1,163 @@
1
+ Given /^this project is active project folder/ do
2
+ @active_project_folder = File.expand_path(File.dirname(__FILE__) + "/../..")
3
+ end
4
+
5
+ Given /^env variable \$([\w_]+) set to "(.*)"/ do |env_var, value|
6
+ ENV[env_var] = value
7
+ end
8
+
9
+ Given /"(.*)" folder is deleted/ do |folder|
10
+ in_project_folder { FileUtils.rm_rf folder }
11
+ end
12
+
13
+ When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments|
14
+ @stdout = StringIO.new
15
+ in_project_folder do
16
+ if Object.const_defined?("APP_ROOT")
17
+ APP_ROOT.replace(FileUtils.pwd)
18
+ else
19
+ APP_ROOT = FileUtils.pwd
20
+ end
21
+ run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout)
22
+ end
23
+ File.open(File.join(@tmp_root, "generator.out"), "w") do |f|
24
+ @stdout.rewind
25
+ f << @stdout.read
26
+ end
27
+ end
28
+
29
+ When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
30
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
31
+ in_project_folder do
32
+ system "#{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
33
+ end
34
+ end
35
+
36
+ When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
37
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
38
+ in_project_folder do
39
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
40
+ end
41
+ end
42
+
43
+ When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
44
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
45
+ executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable))
46
+ in_project_folder do
47
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
48
+ end
49
+ end
50
+
51
+ When /^I invoke task "rake (.*)"/ do |task|
52
+ @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
53
+ in_project_folder do
54
+ system "rake #{task} --trace > #{@stdout} 2> #{@stdout}"
55
+ end
56
+ end
57
+
58
+ Then /^folder "(.*)" (is|is not) created/ do |folder, is|
59
+ in_project_folder do
60
+ File.exists?(folder).should(is == 'is' ? be_true : be_false)
61
+ end
62
+ end
63
+
64
+ Then /^file "(.*)" (is|is not) created/ do |file, is|
65
+ in_project_folder do
66
+ File.exists?(file).should(is == 'is' ? be_true : be_false)
67
+ end
68
+ end
69
+
70
+ Then /^file with name matching "(.*)" is created/ do |pattern|
71
+ in_project_folder do
72
+ Dir[pattern].should_not be_empty
73
+ end
74
+ end
75
+
76
+ Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex|
77
+ in_project_folder do
78
+ actual_output = File.read(file)
79
+ (does == 'does') ?
80
+ actual_output.should(match(/#{regex}/)) :
81
+ actual_output.should_not(match(/#{regex}/))
82
+ end
83
+ end
84
+
85
+ Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file|
86
+ File.exists?(gem_file).should be_true
87
+ File.exists?(project_file).should be_true
88
+ gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}")
89
+ project_file_contents = File.read(File.join(@active_project_folder, project_file))
90
+ project_file_contents.should == gem_file_contents
91
+ end
92
+
93
+ Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
94
+ actual_output = File.read(@stdout)
95
+ does_invoke == "does" ?
96
+ actual_output.should(match(/dependency\s+#{generator}/)) :
97
+ actual_output.should_not(match(/dependency\s+#{generator}/))
98
+ end
99
+
100
+ Then /help options "(.*)" and "(.*)" are displayed/ do |opt1, opt2|
101
+ actual_output = File.read(@stdout)
102
+ actual_output.should match(/#{opt1}/)
103
+ actual_output.should match(/#{opt2}/)
104
+ end
105
+
106
+ Then /^I should see$/ do |text|
107
+ actual_output = File.read(@stdout)
108
+ actual_output.should contain(text)
109
+ end
110
+
111
+ Then /^I should not see$/ do |text|
112
+ actual_output = File.read(@stdout)
113
+ actual_output.should_not contain(text)
114
+ end
115
+
116
+ Then /^I should see exactly$/ do |text|
117
+ actual_output = File.read(@stdout)
118
+ actual_output.should == text
119
+ end
120
+
121
+ Then /^I should see all (\d+) tests pass/ do |expected_test_count|
122
+ expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors}
123
+ actual_output = File.read(@stdout)
124
+ actual_output.should match(expected)
125
+ end
126
+
127
+ Then /^I should see all (\d+) examples pass/ do |expected_test_count|
128
+ expected = %r{^#{expected_test_count} examples?, 0 failures}
129
+ actual_output = File.read(@stdout)
130
+ actual_output.should match(expected)
131
+ end
132
+
133
+ Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml|
134
+ in_project_folder do
135
+ yaml = eval yaml
136
+ YAML.load(File.read(file)).should == yaml
137
+ end
138
+ end
139
+
140
+ Then /^Rakefile can display tasks successfully/ do
141
+ @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out"))
142
+ in_project_folder do
143
+ system "rake -T > #{@stdout} 2> #{@stdout}"
144
+ end
145
+ actual_output = File.read(@stdout)
146
+ actual_output.should match(/^rake\s+\w+\s+#\s.*/)
147
+ end
148
+
149
+ Then /^task "rake (.*)" is executed successfully/ do |task|
150
+ @stdout.should_not be_nil
151
+ actual_output = File.read(@stdout)
152
+ actual_output.should_not match(/^Don't know how to build task '#{task}'/)
153
+ actual_output.should_not match(/Error/i)
154
+ end
155
+
156
+ Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex|
157
+ in_project_folder do
158
+ gem_file = Dir["pkg/*.gem"].first
159
+ gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`)
160
+ spec_value = gem_spec.send(key.to_sym)
161
+ spec_value.to_s.should match(/#{regex}/)
162
+ end
163
+ end
@@ -0,0 +1,29 @@
1
+ module CommonHelpers
2
+ def in_tmp_folder(&block)
3
+ FileUtils.chdir(@tmp_root, &block)
4
+ end
5
+
6
+ def in_project_folder(&block)
7
+ project_folder = @active_project_folder || @tmp_root
8
+ FileUtils.chdir(project_folder, &block)
9
+ end
10
+
11
+ def in_home_folder(&block)
12
+ FileUtils.chdir(@home_path, &block)
13
+ end
14
+
15
+ def force_local_lib_override(project_name = @project_name)
16
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
17
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
18
+ f << "$:.unshift('#{@lib_path}')\n"
19
+ f << rakefile
20
+ end
21
+ end
22
+
23
+ def setup_active_project_folder project_name
24
+ @active_project_folder = File.join(@tmp_root, project_name)
25
+ @project_name = project_name
26
+ end
27
+ end
28
+
29
+ World(CommonHelpers)
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + "/../../lib/system-builder"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
7
+
8
+ Before do
9
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
10
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
11
+ FileUtils.rm_rf @tmp_root
12
+ FileUtils.mkdir_p @home_path
13
+ ENV['HOME'] = @home_path
14
+ end
@@ -0,0 +1,11 @@
1
+ module Matchers
2
+ def contain(expected)
3
+ simple_matcher("contain #{expected.inspect}") do |given, matcher|
4
+ matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
5
+ matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
6
+ given.index expected
7
+ end
8
+ end
9
+ end
10
+
11
+ World(Matchers)
@@ -0,0 +1,23 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module SystemBuilder
5
+ VERSION = '0.0.2'
6
+
7
+ @@configurations = {}
8
+
9
+ def self.config(name, value = nil, &block)
10
+ value = (value or block.call)
11
+ puts "* load configuration #{name}"
12
+ @@configurations[name.to_s] = value
13
+ end
14
+
15
+ def self.configuration(name)
16
+ @@configurations[name.to_s]
17
+ end
18
+ end
19
+
20
+ require 'system_builder/core_ext'
21
+ require 'system_builder/image'
22
+ require 'system_builder/boot'
23
+ require 'system_builder/configurator'
@@ -0,0 +1,191 @@
1
+ class SystemBuilder::DebianBoot
2
+
3
+ attr_accessor :version, :mirror, :architecture
4
+ attr_accessor :exclude, :include
5
+
6
+ attr_reader :root
7
+ attr_reader :configurators
8
+
9
+ def initialize(root)
10
+ @root = root
11
+
12
+ @version = :lenny
13
+ @mirror = 'http://ftp.debian.org/debian'
14
+ @architecture = :i386
15
+ @exclude = []
16
+ @include = []
17
+
18
+ # kernel can't be installed by debootstrap
19
+ @configurators =
20
+ [ localhost_configurator,
21
+ apt_configurator,
22
+ kernel_configurator,
23
+ fstab_configurator,
24
+ timezone_configurator ]
25
+ end
26
+
27
+ def create
28
+ unless File.exists?(root)
29
+ FileUtils::mkdir_p root
30
+ FileUtils::sudo "debootstrap", debbootstrap_options, version, root, mirror
31
+ end
32
+
33
+ unless @configurators.empty?
34
+ chroot do |chroot|
35
+ @configurators.each do |configurator|
36
+ configurator.configure(chroot)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def kernel_configurator
43
+ SystemBuilder::ProcConfigurator.new do |chroot|
44
+ chroot.image.open("/etc/kernel-img.conf") do |f|
45
+ f.puts "do_initrd = yes"
46
+ end
47
+ chroot.apt_install %w{linux-image-2.6-686 grub}
48
+ end
49
+ end
50
+
51
+ def fstab_configurator
52
+ SystemBuilder::ProcConfigurator.new do |chroot|
53
+ chroot.image.open("/etc/fstab") do |f|
54
+ %w{/tmp /var/run /var/log /var/lock /var/tmp}.each do |directory|
55
+ f.puts "tmpfs #{directory} tmpfs defaults,noatime 0 0"
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def timezone_configurator
62
+ SystemBuilder::ProcConfigurator.new do |chroot|
63
+ # Use same timezone than build machine
64
+ chroot.image.install "/etc/", "/etc/timezone", "/etc/localtime"
65
+ end
66
+ end
67
+
68
+ def apt_configurator
69
+ # TODO see if this step is really needed
70
+ SystemBuilder::ProcConfigurator.new do |chroot|
71
+ chroot.image.install "/etc/apt", "/etc/apt/trusted.gpg"
72
+ chroot.sudo "apt-get update"
73
+ end
74
+ end
75
+
76
+ def localhost_configurator
77
+ SystemBuilder::ProcConfigurator.new do |chroot|
78
+ chroot.image.open("/etc/hosts") do |f|
79
+ f.puts "127.0.0.1 localhost"
80
+ f.puts "::1 localhost ip6-localhost ip6-loopback"
81
+ end
82
+ end
83
+ end
84
+
85
+ def configure(&block)
86
+ @configurators << SystemBuilder::ProcConfigurator.new(block)
87
+ end
88
+
89
+ def debbootstrap_options
90
+ {
91
+ :arch => architecture,
92
+ :exclude => exclude.join(','),
93
+ :include => include.join(','),
94
+ :variant => :minbase
95
+ }.collect do |k,v|
96
+ ["--#{k}", Array(v).join(',')] unless v.blank?
97
+ end.compact
98
+ end
99
+
100
+ def image(&block)
101
+ @image ||= Image.new(root)
102
+
103
+ if block_given?
104
+ yield @image
105
+ else
106
+ @image
107
+ end
108
+ end
109
+
110
+ def chroot(&block)
111
+ @chroot ||= Chroot.new(image)
112
+ @chroot.execute &block
113
+ end
114
+
115
+ class Image
116
+
117
+ def initialize(root)
118
+ @root = root
119
+ end
120
+
121
+ def mkdir(directory)
122
+ FileUtils::sudo "mkdir -p #{expand_path(directory)}"
123
+ end
124
+
125
+ def install(target, *sources)
126
+ FileUtils::sudo "cp --preserve=mode,timestamps #{sources.join(' ')} #{expand_path(target)}"
127
+ end
128
+
129
+ def rsync(target, *sources)
130
+ options = (Hash === sources.last ? sources.pop : {})
131
+ rsync_options = options.collect { |k,v| "--#{k}=#{v}" }
132
+ FileUtils::sudo "rsync -av #{rsync_options.join(' ')} #{sources.join(' ')} #{expand_path(target)}"
133
+ end
134
+
135
+ def open(filename, &block)
136
+ Tempfile.open(File.basename(filename)) do |f|
137
+ yield f
138
+ f.close
139
+
140
+ File.chmod 0644, f.path
141
+ install filename, f.path
142
+ end
143
+ end
144
+
145
+ def expand_path(path)
146
+ File.join(@root,path)
147
+ end
148
+
149
+ end
150
+
151
+ class Chroot
152
+
153
+ attr_reader :image
154
+
155
+ def initialize(image)
156
+ @image = image
157
+ end
158
+
159
+ def apt_install(*packages)
160
+ sudo "apt-get install --yes --force-yes #{packages.join(' ')}"
161
+ end
162
+
163
+ def cp(*arguments)
164
+ sudo "cp #{arguments.join(' ')}"
165
+ end
166
+
167
+ def sh(*arguments)
168
+ FileUtils::sudo "chroot #{image.expand_path('/')} sh -c \"LC_ALL=C #{arguments.join(' ')}\""
169
+ end
170
+ alias_method :sudo, :sh
171
+
172
+ def execute(&block)
173
+ begin
174
+ prepare_run
175
+ yield self
176
+ ensure
177
+ unprepare_run
178
+ end
179
+ end
180
+
181
+ def prepare_run
182
+ FileUtils::sudo "mount proc #{image.expand_path('/proc')} -t proc"
183
+ end
184
+
185
+ def unprepare_run
186
+ FileUtils::sudo "umount #{image.expand_path('/proc')}"
187
+ end
188
+
189
+ end
190
+
191
+ end
@@ -0,0 +1,35 @@
1
+ require 'optparse'
2
+
3
+ module SystemBuilder
4
+ class CLI
5
+ def self.execute(stdout, arguments=[])
6
+ options = {}
7
+ mandatory_options = %w(config)
8
+
9
+ OptionParser.new do |opts|
10
+ opts.banner = <<-BANNER.gsub(/^ /,'')
11
+ Create and configure bootable systems
12
+
13
+ Usage: #{File.basename($0)} [options] image command
14
+
15
+ Options are:
16
+ BANNER
17
+ opts.separator ""
18
+ opts.on("-c", "--config=FILE", String,
19
+ "The file containing image and boot definitions") { |arg| options[:config] = arg }
20
+ opts.on("-h", "--help", "Show this help message.") { stdout.puts opts; exit }
21
+
22
+ opts.parse!(arguments)
23
+
24
+ if mandatory_options && mandatory_options.find { |option| options[option.to_sym].nil? }
25
+ stdout.puts opts; exit
26
+ end
27
+ end
28
+
29
+ load options[:config]
30
+
31
+ image = SystemBuilder.configuration(arguments.unshift)
32
+ image.create
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ module SystemBuilder
2
+
3
+ class ProcConfigurator
4
+
5
+ def initialize(proc = nil, &block)
6
+ @proc = (proc or block)
7
+ end
8
+
9
+ def configure(chroot)
10
+ @proc.call chroot
11
+ end
12
+
13
+ end
14
+
15
+ class PuppetConfigurator
16
+
17
+ attr_reader :manifest
18
+
19
+ def initialize(manifest = ".")
20
+ @manifest = manifest
21
+ end
22
+
23
+ def configure(chroot)
24
+ chroot.apt_install :puppet
25
+ chroot.image.open("/etc/default/puppet") do |f|
26
+ f.puts "START=no"
27
+ end
28
+
29
+ unless File.directory?(manifest)
30
+ chroot.image.install "/tmp/puppet.pp", manifest
31
+ chroot.sudo "puppet tmp/puppet.pp"
32
+ else
33
+ chroot.image.mkdir "/tmp/puppet"
34
+
35
+ chroot.image.rsync "/tmp/puppet", "#{manifest}/manifests", "#{manifest}/files", :exclude => "*~"
36
+ chroot.sudo "puppet tmp/puppet/manifests/site.pp"
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,53 @@
1
+ class Numeric
2
+
3
+ def megabytes
4
+ self * 1024 * 1024
5
+ end
6
+ alias_method :megabyte, :megabytes
7
+
8
+ def in_megabytes
9
+ self.to_f / 1.megabyte
10
+ end
11
+
12
+ end
13
+
14
+ class Object
15
+
16
+ def blank?
17
+ false
18
+ end
19
+
20
+ end
21
+
22
+ class NilClass
23
+
24
+ def blank?
25
+ true
26
+ end
27
+
28
+ end
29
+
30
+ class Array
31
+
32
+ alias_method :blank?, :empty?
33
+
34
+ end
35
+
36
+ class String
37
+
38
+ alias_method :blank?, :empty?
39
+
40
+ end
41
+
42
+ module FileUtils
43
+
44
+ def self.sh(*cmd)
45
+ puts "* #{cmd.join(' ')}"
46
+ raise "Command failed: #{$?}" unless system(cmd.join(' '))
47
+ end
48
+
49
+ def self.sudo(*cmd)
50
+ sh ["sudo", *cmd]
51
+ end
52
+
53
+ end
@@ -0,0 +1,118 @@
1
+ require 'tempfile'
2
+
3
+ class SystemBuilder::DiskImage
4
+
5
+ attr_accessor :boot, :size
6
+ attr_reader :file
7
+
8
+ def initialize(file)
9
+ @file = file
10
+ @size = 512.megabytes
11
+ end
12
+
13
+ def create
14
+ boot.create
15
+ # TODO
16
+
17
+ file_creation = (not File.exists?(file))
18
+ if file_creation
19
+ create_file
20
+ create_partition_table
21
+ format_root_fs
22
+ end
23
+
24
+ install_grub_files :stage_files => %w{e2fs_stage1_5 stage?}
25
+
26
+ sync_root_fs
27
+ install_grub if file_creation
28
+
29
+ self
30
+ end
31
+
32
+ def create_file
33
+ FileUtils::sh "dd if=/dev/zero of=#{file} count=#{size.in_megabytes.to_i} bs=1M"
34
+ end
35
+
36
+ def create_partition_table
37
+ FileUtils::sh "echo '63,' | /sbin/sfdisk --no-reread -uS -H16 -S63 #{file}"
38
+ end
39
+
40
+ def format_root_fs
41
+ loop_device = "/dev/loop0"
42
+ begin
43
+ FileUtils::sudo "losetup -o #{fs_offset} #{loop_device} #{file}"
44
+ FileUtils::sudo "mke2fs -L #{fs_label} -jqF #{loop_device} #{fs_block_size}"
45
+ ensure
46
+ FileUtils::sudo "losetup -d #{loop_device}"
47
+ end
48
+ end
49
+
50
+ def sync_root_fs
51
+ mount_dir = "/tmp/mount_root_fs"
52
+ FileUtils::mkdir_p mount_dir
53
+
54
+ begin
55
+ FileUtils::sudo "mount -o loop,offset=#{fs_offset} #{file} #{mount_dir}"
56
+ FileUtils::sudo "rsync -av #{boot.root}/ #{mount_dir}"
57
+ ensure
58
+ FileUtils::sudo "umount #{mount_dir}"
59
+ end
60
+
61
+ FileUtils.touch file
62
+ end
63
+
64
+ def install_grub_files(options = {})
65
+ stage_files = Array(options[:stage_files]).flatten
66
+
67
+ boot.image do |image|
68
+ image.mkdir "/boot/grub"
69
+
70
+ install_grub_menu options
71
+ image.install "boot/grub", stage_files.collect { |f| '/usr/lib/grub/**/' + f }
72
+ end
73
+ end
74
+
75
+ def install_grub
76
+ IO.popen("sudo grub --device-map=/dev/null","w") { |grub|
77
+ grub.puts "device (hd0) #{file}"
78
+ grub.puts "root (hd0,0)"
79
+ grub.puts "setup (hd0)"
80
+ grub.puts "quit"
81
+ }
82
+ end
83
+
84
+ def install_grub_menu(options = {})
85
+ root = (options[:root] or "LABEL=#{fs_label}")
86
+ version = (options[:version] or Time.now.strftime("%Y%m%d%H%M"))
87
+
88
+ boot.image.open("/boot/grub/menu.lst") do |f|
89
+ f.puts "default 0"
90
+ f.puts "timeout 2"
91
+ f.puts "title #{version} Debian GNU/Linux"
92
+ f.puts "kernel /vmlinuz root=#{root} ro"
93
+ f.puts "initrd /initrd.img"
94
+ end
95
+ end
96
+
97
+ def convert(export_file, options = {})
98
+ unless FileUtils.uptodate? export_file, file
99
+ arguments = []
100
+ arguments << "-O #{options[:format]}" if options[:format]
101
+ FileUtils::sh "qemu-img convert -f raw #{file} #{arguments.join(' ')} #{export_file}"
102
+ end
103
+ end
104
+
105
+ def fs_block_size
106
+ linux_partition_info = `/sbin/sfdisk -l #{file}`.scan(%r{#{file}.*Linux}).first
107
+ linux_partition_info.split[4].to_i
108
+ end
109
+
110
+ def fs_offset
111
+ 32256
112
+ end
113
+
114
+ def fs_label
115
+ "root"
116
+ end
117
+
118
+ end
@@ -0,0 +1,36 @@
1
+ require 'rake/tasklib'
2
+
3
+ class SystemBuilder::Task < Rake::TaskLib
4
+
5
+ attr_reader :name
6
+
7
+ def initialize(name, &block)
8
+ @name = name
9
+
10
+ @image =
11
+ if block_given?
12
+ block.call
13
+ else
14
+ SystemBuilder.config(name)
15
+ end
16
+
17
+ define
18
+ end
19
+
20
+ def define
21
+ namespace name do
22
+ desc "Create image #{name} in #{@image.file}"
23
+ task :dist do
24
+ @image.create
25
+ end
26
+ namespace :dist do
27
+ desc "Create vmwaire image in #{@image.file}.vdmk"
28
+ task :vmware do
29
+ @image.convert "#{@image.file}.vmdk", :format => "vmdk"
30
+ end
31
+ end
32
+ task "dist:vmware" => "dist"
33
+ end
34
+ end
35
+
36
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/system-builder.rb'}"
9
+ puts "Loading system-builder gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'system-builder'
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'system-builder/cli'
3
+
4
+ describe SystemBuilder::CLI, "execute" do
5
+ before(:each) do
6
+ @stdout_io = StringIO.new
7
+ SystemBuilder::CLI.execute(@stdout_io, [])
8
+ @stdout_io.rewind
9
+ @stdout = @stdout_io.read
10
+ end
11
+
12
+ it "should print default output" do
13
+ @stdout.should =~ /To update this executable/
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ # Time to add your specs!
4
+ # http://rspec.info/
5
+ describe "Place your specs here" do
6
+
7
+ it "find this spec in spec directory" do
8
+ # violated "Be sure to write your specs"
9
+ end
10
+
11
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{system-builder}
5
+ s.version = "0.0.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alban Peignier"]
9
+ s.date = %q{2009-09-02}
10
+ s.default_executable = %q{system-builder}
11
+ s.description = %q{FIX (describe your package)}
12
+ s.email = ["alban.peignier@free.fr"]
13
+ s.executables = ["system-builder"]
14
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt"]
15
+ s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "bin/system-builder", "examples/Rakefile", "examples/config.rb", "examples/manifests/classes/test.pp", "examples/manifests/site.pp", "features/development.feature", "features/step_definitions/common_steps.rb", "features/support/common.rb", "features/support/env.rb", "features/support/matchers.rb", "lib/system_builder.rb", "lib/system_builder/boot.rb", "lib/system_builder/cli.rb", "lib/system_builder/configurator.rb", "lib/system_builder/core_ext.rb", "lib/system_builder/image.rb", "lib/system_builder/task.rb", "script/console", "script/destroy", "script/generate", "spec/spec.opts", "spec/spec_helper.rb", "spec/system-builder_cli_spec.rb", "spec/system-builder_spec.rb", "system-builder.gemspec", "tasks/rspec.rake"]
16
+ s.homepage = %q{http://github.com/#{github_username}/#{project_name}}
17
+ s.rdoc_options = ["--main", "README.rdoc"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{system-builder}
20
+ s.rubygems_version = %q{1.3.4}
21
+ s.summary = %q{FIX (describe your package)}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_development_dependency(%q<hoe>, [">= 2.3.3"])
29
+ else
30
+ s.add_dependency(%q<hoe>, [">= 2.3.3"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<hoe>, [">= 2.3.3"])
34
+ end
35
+ end
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: albanpeignier-system-builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Alban Peignier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-02 00:00:00 -07:00
13
+ default_executable: system-builder
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.3
24
+ version:
25
+ description: FIX (describe your package)
26
+ email:
27
+ - alban.peignier@free.fr
28
+ executables:
29
+ - system-builder
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - PostInstall.txt
36
+ files:
37
+ - History.txt
38
+ - Manifest.txt
39
+ - PostInstall.txt
40
+ - README.rdoc
41
+ - Rakefile
42
+ - bin/system-builder
43
+ - examples/Rakefile
44
+ - examples/config.rb
45
+ - examples/manifests/classes/test.pp
46
+ - examples/manifests/site.pp
47
+ - features/development.feature
48
+ - features/step_definitions/common_steps.rb
49
+ - features/support/common.rb
50
+ - features/support/env.rb
51
+ - features/support/matchers.rb
52
+ - lib/system_builder.rb
53
+ - lib/system_builder/boot.rb
54
+ - lib/system_builder/cli.rb
55
+ - lib/system_builder/configurator.rb
56
+ - lib/system_builder/core_ext.rb
57
+ - lib/system_builder/image.rb
58
+ - lib/system_builder/task.rb
59
+ - script/console
60
+ - script/destroy
61
+ - script/generate
62
+ - spec/spec.opts
63
+ - spec/spec_helper.rb
64
+ - spec/system-builder_cli_spec.rb
65
+ - spec/system-builder_spec.rb
66
+ - system-builder.gemspec
67
+ - tasks/rspec.rake
68
+ has_rdoc: false
69
+ homepage: http://github.com/#{github_username}/#{project_name}
70
+ post_install_message:
71
+ rdoc_options:
72
+ - --main
73
+ - README.rdoc
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ requirements: []
89
+
90
+ rubyforge_project: system-builder
91
+ rubygems_version: 1.2.0
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: FIX (describe your package)
95
+ test_files: []
96
+