kameleon-builder 2.0.0 → 2.1.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/.editorconfig +0 -0
- data/.env +63 -15
- data/.gitignore +1 -0
- data/README.rst +4 -2
- data/Vagrantfile +13 -52
- data/bin/kameleon +5 -0
- data/completion/_kameleon.zsh +18 -0
- data/completion/kameleon.bash +13 -0
- data/completion/kameleon.fish +10 -0
- data/contrib/polipo_env.sh +2 -0
- data/contrib/steps/export/save_as_g5k.yaml +63 -0
- data/contrib/steps/setup/add_to_sudoers.yaml +5 -0
- data/docs/Makefile +10 -6
- data/docs/README.md +17 -0
- data/docs/source/_static/kameleon-logo.png +0 -0
- data/docs/source/_static/kameleon-logo.xcf +0 -0
- data/docs/source/_static/kameleon-long.png +0 -0
- data/docs/source/aliases.rst +4 -2
- data/docs/source/checkpoint.rst +2 -0
- data/docs/source/commands.rst +4 -3
- data/docs/source/conf.py +15 -7
- data/docs/source/context.rst +7 -4
- data/docs/source/faq.rst +39 -1
- data/docs/source/getting_started.rst +227 -1
- data/docs/source/grid5000_tutorial.rst +110 -0
- data/docs/source/index.rst +7 -2
- data/docs/source/installation.rst +12 -4
- data/docs/source/persistent_cache.rst +34 -0
- data/docs/source/recipe.rst +23 -16
- data/docs/source/use_cases.rst +93 -0
- data/docs/source/workspace.rst +2 -0
- data/kameleon-builder.gemspec +7 -1
- data/lib/kameleon.rb +3 -6
- data/lib/kameleon/cli.rb +104 -50
- data/lib/kameleon/compat.rb +39 -0
- data/lib/kameleon/context.rb +43 -13
- data/lib/kameleon/engine.rb +118 -77
- data/lib/kameleon/environment.rb +3 -5
- data/lib/kameleon/error.rb +15 -9
- data/lib/kameleon/logger.rb +7 -4
- data/lib/kameleon/persistent_cache.rb +139 -0
- data/lib/kameleon/recipe.rb +200 -81
- data/lib/kameleon/shell.rb +51 -16
- data/omnibus/.gitignore +11 -0
- data/omnibus/.kitchen.yml +25 -0
- data/omnibus/Berksfile +9 -0
- data/omnibus/Berksfile.lock +25 -0
- data/omnibus/Gemfile +12 -0
- data/omnibus/README.md +94 -0
- data/omnibus/config/projects/kameleon.rb +23 -0
- data/omnibus/config/software/kameleon.rb +24 -0
- data/omnibus/config/software/polipo.rb +30 -0
- data/omnibus/config/software/ruby.rb +158 -0
- data/omnibus/files/mac_dmg/Resources/background.png +0 -0
- data/omnibus/files/mac_dmg/Resources/icon.png +0 -0
- data/omnibus/files/mac_pkg/Resources/background.png +0 -0
- data/omnibus/files/mac_pkg/Resources/license.html +1 -0
- data/omnibus/files/mac_pkg/Resources/welcome.html +9 -0
- data/omnibus/omnibus.rb +27 -0
- data/omnibus/package-scripts/kameleon/makeselfinst +27 -0
- data/omnibus/package-scripts/kameleon/postrm +9 -0
- data/templates/archlinux-desktop.yaml +25 -0
- data/templates/archlinux.yaml +106 -0
- data/templates/debian-testing.yaml +25 -0
- data/templates/debian7-desktop.yaml +25 -0
- data/templates/{debian-wheezy-docker.yaml → debian7-docker.yaml} +30 -16
- data/templates/debian7-g5k.yaml +97 -0
- data/templates/debian7-oar-dev.yaml +51 -0
- data/templates/debian7.yaml +128 -0
- data/templates/extend.erb +23 -0
- data/templates/fedora-rawhide.yaml +30 -0
- data/templates/fedora20-desktop.yaml +21 -0
- data/templates/fedora20.yaml +105 -0
- data/templates/{debian-wheezy-chroot.yaml → old-debian7.yaml} +51 -38
- data/templates/{aliases → steps/aliases}/defaults.yaml +37 -12
- data/templates/steps/bootstrap/archlinux/arch_bootstrap.yaml +219 -0
- data/templates/steps/bootstrap/archlinux/install_bootloader.yaml +46 -0
- data/templates/steps/bootstrap/archlinux/populate_disk.yaml +39 -0
- data/templates/steps/bootstrap/debian/debootstrap.yaml +18 -10
- data/templates/steps/bootstrap/debian/debootstrap_arm.yaml +31 -0
- data/templates/steps/bootstrap/fedora/liveos_bootstrap.yaml +123 -0
- data/templates/steps/bootstrap/g5k_reserv.yaml +70 -0
- data/templates/steps/bootstrap/initialize_disk_chroot.yaml +84 -0
- data/templates/steps/bootstrap/initialize_disk_qemu.yaml +72 -0
- data/templates/steps/bootstrap/install_bootloader.yaml +42 -0
- data/templates/steps/bootstrap/prepare_chroot.yaml +126 -0
- data/templates/steps/bootstrap/prepare_docker.yaml +19 -8
- data/templates/steps/bootstrap/prepare_qemu.yaml +47 -0
- data/templates/steps/bootstrap/start_chroot.yaml +11 -2
- data/templates/steps/bootstrap/start_docker.yaml +2 -2
- data/templates/steps/bootstrap/start_qemu.yaml +75 -0
- data/templates/steps/bootstrap/ubuntu/debootstrap.yaml +27 -0
- data/templates/steps/breakpoint.yaml +2 -0
- data/templates/{checkpoints → steps/checkpoints}/docker.yaml +0 -0
- data/templates/steps/checkpoints/qcow2.yaml +38 -0
- data/templates/steps/checkpoints/qemu.yaml +39 -0
- data/templates/steps/export/clean_appliance.yaml +7 -1
- data/templates/steps/export/compact_qcow_img.yaml +12 -0
- data/templates/steps/export/save_appliance.yaml +58 -0
- data/templates/steps/export/save_appliance_from_g5k.yaml +47 -0
- data/templates/steps/export/save_vagrant_box.yaml +29 -0
- data/templates/steps/setup/archlinux/configure_keyboard.yaml +9 -0
- data/templates/steps/setup/archlinux/configure_network.yaml +9 -0
- data/templates/steps/setup/archlinux/configure_ruby.yaml +7 -0
- data/templates/steps/setup/archlinux/configure_system.yaml +20 -0
- data/templates/steps/setup/archlinux/install_dev_tools.yaml +18 -0
- data/templates/steps/setup/archlinux/install_gnome.yaml +27 -0
- data/templates/steps/setup/archlinux/install_software.yaml +9 -0
- data/templates/steps/setup/archlinux/install_yaourt.yaml +29 -0
- data/templates/steps/setup/autologin.yaml +16 -0
- data/templates/steps/setup/create_group.yaml +12 -0
- data/templates/steps/setup/create_user.yaml +9 -10
- data/templates/steps/setup/debian/configure_apt.yaml +65 -0
- data/templates/steps/setup/debian/configure_kernel.yaml +18 -0
- data/templates/steps/setup/debian/{keyboard_config.yaml → configure_keyboard.yaml} +1 -1
- data/templates/steps/setup/debian/{network_config.yaml → configure_network.yaml} +0 -0
- data/templates/steps/setup/debian/{system_config.yaml → configure_system.yaml} +0 -0
- data/templates/steps/setup/debian/install_gnome.yaml +13 -0
- data/templates/steps/setup/debian/install_kde.yaml +13 -0
- data/templates/steps/setup/debian/install_software.yaml +2 -0
- data/templates/steps/setup/debian/oar/oar_debian_config_frontend.yaml +8 -0
- data/templates/steps/setup/debian/oar/oar_debian_config_node.yaml +5 -0
- data/templates/steps/setup/debian/oar/oar_debian_config_server.yaml +5 -0
- data/templates/steps/setup/debian/oar/oar_prereq_install.yaml +16 -0
- data/templates/steps/setup/debian/setup_vagrant_box.yaml +52 -0
- data/templates/steps/setup/debian/upgrade_system.yaml +15 -0
- data/templates/steps/setup/fedora/configure_network.yaml +30 -0
- data/templates/steps/setup/fedora/configure_system.yaml +59 -0
- data/templates/steps/setup/fedora/install_software.yaml +3 -0
- data/templates/steps/setup/fedora/update_system.yaml +10 -0
- data/templates/steps/setup/oar/oar_config_devel.yaml +21 -0
- data/templates/steps/setup/oar/oar_config_frontend.yaml +38 -0
- data/templates/steps/setup/oar/oar_config_node.yaml +4 -0
- data/templates/steps/setup/oar/oar_config_server.yaml +25 -0
- data/templates/steps/setup/oar/oar_config_system.yaml +34 -0
- data/templates/steps/setup/oar/oar_devel_prereq_install.yaml +5 -0
- data/templates/steps/setup/oar/oar_git_install.yaml +21 -0
- data/templates/steps/setup/ubuntu/configure_apt.yaml +67 -0
- data/templates/ubuntu-12.04-desktop.yaml +25 -0
- data/templates/ubuntu-12.04.yaml +128 -0
- data/templates/ubuntu-14.04-desktop.yaml +27 -0
- data/templates/ubuntu-14.04.yaml +25 -0
- data/templates/vagrant-debian7.yaml +31 -0
- data/version.txt +1 -1
- metadata +155 -28
- checksums.yaml +0 -7
- data/templates/checkpoints/qcow2.yaml +0 -44
- data/templates/fedora-docker.yaml +0 -96
- data/templates/steps/bootstrap/fedora/docker_bootstrap.yaml +0 -25
- data/templates/steps/bootstrap/fedora/yum_bootstrap.yaml +0 -22
- data/templates/steps/bootstrap/prepare_appliance_with_nbd.yaml +0 -93
- data/templates/steps/export/build_appliance_from_docker.yaml +0 -105
- data/templates/steps/export/save_appliance_from_nbd.yaml +0 -54
- data/templates/steps/setup/debian/kernel_install.yaml +0 -20
- data/templates/steps/setup/debian/software_install.yaml +0 -15
- data/templates/steps/setup/fedora/kernel_install.yaml +0 -27
- data/templates/steps/setup/fedora/software_install.yaml +0 -10
data/lib/kameleon/environment.rb
CHANGED
@@ -6,23 +6,21 @@ module Kameleon
|
|
6
6
|
|
7
7
|
attr_accessor :workspace
|
8
8
|
attr_accessor :templates_path
|
9
|
-
attr_accessor :recipes_path
|
10
9
|
attr_accessor :build_path
|
11
10
|
attr_accessor :log_file
|
12
11
|
attr_accessor :debug
|
13
12
|
|
14
13
|
|
15
14
|
def initialize(options = {})
|
16
|
-
@logger = Log4r::Logger.new("kameleon::[
|
15
|
+
@logger = Log4r::Logger.new("kameleon::[kameleon]")
|
17
16
|
# symbolify commandline options
|
18
17
|
options = options.inject({}) {|result,(key,value)| result.update({key.to_sym => value})}
|
19
|
-
workspace = File.expand_path(
|
20
|
-
build_path = File.expand_path(options[:build_path] || File.join(workspace, "
|
18
|
+
workspace = File.expand_path(Dir.pwd)
|
19
|
+
build_path = File.expand_path(options[:build_path] || File.join(workspace, "build"))
|
21
20
|
defaults = {
|
22
21
|
:workspace => Pathname.new(workspace),
|
23
22
|
:templates_path => Kameleon.templates_path,
|
24
23
|
:templates_names => Kameleon.templates_names,
|
25
|
-
:recipes_path => Pathname.new(File.join(workspace, "recipes")),
|
26
24
|
:build_path => Pathname.new(build_path),
|
27
25
|
:log_file => Pathname.new(File.join(workspace, "kameleon.log"))
|
28
26
|
}
|
data/lib/kameleon/error.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'thor/error'
|
2
2
|
|
3
3
|
module Kameleon
|
4
|
-
class
|
4
|
+
class Error < ::StandardError
|
5
5
|
attr_accessor :object
|
6
6
|
|
7
7
|
def initialize(message=nil, object=nil)
|
@@ -14,17 +14,19 @@ module Kameleon
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
class
|
18
|
-
class
|
19
|
-
class
|
20
|
-
class
|
21
|
-
class
|
22
|
-
class
|
23
|
-
class
|
17
|
+
class KameleonError < Error; status_code(1) ; end
|
18
|
+
class ExecError < Error; status_code(2) ; end
|
19
|
+
class InternalError < Error; status_code(3) ; end
|
20
|
+
class ContextError < Error; status_code(4) ; end
|
21
|
+
class ShellError < Error; status_code(5) ; end
|
22
|
+
class RecipeError < Error; status_code(6) ; end
|
23
|
+
class BuildError < Error; status_code(7) ; end
|
24
|
+
class AbortError < Error; status_code(8) ; end
|
25
|
+
class TemplateNotFound < Error; status_code(9) ; end
|
24
26
|
|
25
27
|
def self.with_friendly_errors
|
26
28
|
yield
|
27
|
-
rescue Kameleon::
|
29
|
+
rescue Kameleon::Error => e
|
28
30
|
e.message.split( /\r?\n/ ).each {|m| Kameleon.logger.fatal m }
|
29
31
|
exit e.status_code
|
30
32
|
rescue Thor::UndefinedTaskError => e
|
@@ -38,6 +40,10 @@ module Kameleon
|
|
38
40
|
rescue SystemExit, Interrupt => e
|
39
41
|
Kameleon.logger.fatal("Quitting...")
|
40
42
|
exit 1
|
43
|
+
rescue Errno::ENOENT => e
|
44
|
+
$stderr << "#{e.message}\n"
|
45
|
+
e.backtrace.each {|m| Kameleon.logger.debug m }
|
46
|
+
exit 16
|
41
47
|
rescue Exception => e
|
42
48
|
if ENV["KAMELEON_LOG"] != "debug"
|
43
49
|
$stderr << "Unfortunately, a fatal error has occurred : "\
|
data/lib/kameleon/logger.rb
CHANGED
@@ -14,7 +14,7 @@ module Kameleon
|
|
14
14
|
def format(event)
|
15
15
|
buff = sprintf(@@basicformat, @max_level_length, event.name)
|
16
16
|
buff << (event.tracer.nil? ? "" : "(#{event.tracer[0]})") + ": "
|
17
|
-
unless Log4r::LNAMES[event.level].
|
17
|
+
unless Log4r::LNAMES[event.level].include? "PROGRESS"
|
18
18
|
@on_progress = false
|
19
19
|
buff << format_object(event.data) + "\n"
|
20
20
|
else
|
@@ -30,13 +30,14 @@ module Kameleon
|
|
30
30
|
|
31
31
|
# Custom Log4r formatter for files
|
32
32
|
class FileFormatter < Log4r::BasicFormatter
|
33
|
-
|
33
|
+
@@basicformat = "%*s"
|
34
34
|
def initialize(hash={})
|
35
35
|
super(hash)
|
36
|
+
@max_level_length = 11
|
36
37
|
end
|
37
38
|
|
38
39
|
def format(logevent)
|
39
|
-
if Log4r::LNAMES[logevent.level].
|
40
|
+
if Log4r::LNAMES[logevent.level].include? "PROGRESS"
|
40
41
|
# Formats the data as is with no newline, to allow progress bars to be logged.
|
41
42
|
sprintf("%s", logevent.data.to_s)
|
42
43
|
else
|
@@ -46,7 +47,9 @@ module Kameleon
|
|
46
47
|
# Prevent two newlines in the log file
|
47
48
|
logevent.data.chop! if logevent.data =~ /\n$/
|
48
49
|
end
|
49
|
-
sprintf(
|
50
|
+
tracer = sprintf(@@basicformat, @max_level_length, logevent.name)
|
51
|
+
tracer << (logevent.tracer.nil? ? "" : "(#{logevent.tracer[0]})") + ": "
|
52
|
+
sprintf("%s %s\n", tracer, format_object(logevent.data))
|
50
53
|
end
|
51
54
|
end
|
52
55
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'childprocess'
|
2
|
+
require 'singleton'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module Kameleon
|
6
|
+
#This ruby class will control the execution of Polipo web proxy
|
7
|
+
class Persistent_cache
|
8
|
+
|
9
|
+
include Singleton
|
10
|
+
attr_reader :polipo_env, :cache_dir,:polipo_port
|
11
|
+
attr_writer :activated, :cwd, :polipo_path, :name
|
12
|
+
def initialize()
|
13
|
+
@logger = Log4r::Logger.new("kameleon::[kameleon]")
|
14
|
+
## we must configure Polipo to be execute for the in and out context
|
15
|
+
## we have to start polipo in the out context for debootstrap step
|
16
|
+
|
17
|
+
@polipo_env = File.join(Kameleon.source_root,
|
18
|
+
"contrib",
|
19
|
+
"polipo_env.sh")
|
20
|
+
|
21
|
+
@polipo_process = nil
|
22
|
+
@polipo_port = find_unused_port
|
23
|
+
|
24
|
+
@polipo_cmd_options = {:diskCacheRoot => "",
|
25
|
+
:idleTime => "5",
|
26
|
+
:chunkHighMark => "425165824",
|
27
|
+
:proxyPort => @polipo_port,
|
28
|
+
#:proxyOffline => "true"
|
29
|
+
:relaxTransparency =>"true"
|
30
|
+
}
|
31
|
+
|
32
|
+
@activated = false
|
33
|
+
|
34
|
+
@cache_dir = ""
|
35
|
+
@polipo_path = nil
|
36
|
+
@cwd = ""
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_unused_port
|
41
|
+
ports = (8000..9000)
|
42
|
+
port = 0
|
43
|
+
tmp = nil
|
44
|
+
ports.each do |p|
|
45
|
+
begin
|
46
|
+
port = p
|
47
|
+
tmp = TCPServer.new('localhost',port)
|
48
|
+
rescue
|
49
|
+
port =0
|
50
|
+
end
|
51
|
+
break if(port>0)
|
52
|
+
end
|
53
|
+
tmp.close
|
54
|
+
port
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_polipo_binary
|
58
|
+
|
59
|
+
|
60
|
+
@polipo_path ||= which("polipo")
|
61
|
+
|
62
|
+
if @polipo_path.nil? then
|
63
|
+
@logger.error("Polipo binary not found, make sure it is in your current PATH")
|
64
|
+
@logger.error("or use the option --proxy_path")
|
65
|
+
raise BuildError, "Failed to use persistent cache"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def activated?
|
70
|
+
@activated
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def cwd=(dir)
|
75
|
+
@cwd = dir
|
76
|
+
@cache_dir = @cwd + "/cache/"
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_cache_directory(step_name)
|
80
|
+
@logger.notice("Creating cache directory #{step_name} for Polipo")
|
81
|
+
directory_name = @cache_dir + "/#{step_name}"
|
82
|
+
FileUtils.mkdir_p directory_name
|
83
|
+
directory_name
|
84
|
+
end
|
85
|
+
|
86
|
+
def start_web_proxy_in(directory)
|
87
|
+
## This function assumes that the cache directory has already been created by the engine
|
88
|
+
## Stopping first the previous proxy
|
89
|
+
## have to check if polipo is running
|
90
|
+
@logger.notice("Starting web proxy Polipo in directory #{directory} using port: #{@polipo_port}")
|
91
|
+
@polipo_process.stop unless @polipo_process.nil?
|
92
|
+
command = ["#{@polipo_path}/polipo"]
|
93
|
+
@polipo_cmd_options[:diskCacheRoot] = directory
|
94
|
+
@polipo_cmd_options.each{ |v,k| command.push("#{v}=#{k}") }
|
95
|
+
ChildProcess.posix_spawn = true
|
96
|
+
@polipo_process = ChildProcess.build(*command)
|
97
|
+
@polipo_process.io.stdout = Tempfile.new("polipo_output")
|
98
|
+
@polipo_process.start
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def stop_web_proxy
|
103
|
+
@polipo_process.stop
|
104
|
+
@logger.notice("Stopping web proxy polipo")
|
105
|
+
end
|
106
|
+
|
107
|
+
def pack()
|
108
|
+
@logger.notice("Packing up the generated cache in #{@cwd}")
|
109
|
+
execute("tar","-cf #{@name}-cache.tar cache/",@cwd)
|
110
|
+
# The cache directory cannot be deleted due to the checkpoints
|
111
|
+
end
|
112
|
+
|
113
|
+
def unpack(cache_path)
|
114
|
+
@logger.notice("Unpacking persistent cache: #{cache_path}")
|
115
|
+
execute("tar","-xf #{cache_path} -C #{@cwd}")
|
116
|
+
end
|
117
|
+
|
118
|
+
def execute(cmd,args,dir=nil)
|
119
|
+
command = [cmd ] + args.split(" ")
|
120
|
+
# @logger.notice(" command generated: #{command}")
|
121
|
+
process = ChildProcess.build(*command)
|
122
|
+
process.cwd = dir unless dir.nil?
|
123
|
+
process.start
|
124
|
+
process.wait
|
125
|
+
end
|
126
|
+
|
127
|
+
def which(cmd)
|
128
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
129
|
+
exe = File.join(path, "#{cmd}")
|
130
|
+
return path if File.executable? exe
|
131
|
+
end
|
132
|
+
return nil
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
data/lib/kameleon/recipe.rb
CHANGED
@@ -8,7 +8,7 @@ module Kameleon
|
|
8
8
|
:checkpoint, :checkpoint_path, :metainfo
|
9
9
|
|
10
10
|
def initialize(path)
|
11
|
-
@logger = Log4r::Logger.new("kameleon::[
|
11
|
+
@logger = Log4r::Logger.new("kameleon::[kameleon]")
|
12
12
|
@path = Pathname.new(path)
|
13
13
|
@name = (@path.basename ".yaml").to_s
|
14
14
|
@recipe_content = File.open(@path, 'r') { |f| f.read }
|
@@ -17,7 +17,6 @@ module Kameleon
|
|
17
17
|
"setup" => Section.new("setup"),
|
18
18
|
"export" => Section.new("export"),
|
19
19
|
}
|
20
|
-
@required_global = %w(out_context in_context)
|
21
20
|
kameleon_id = SecureRandom.uuid
|
22
21
|
@global = {
|
23
22
|
"kameleon_recipe_name" => @name,
|
@@ -25,29 +24,32 @@ module Kameleon
|
|
25
24
|
"kameleon_uuid" => kameleon_id,
|
26
25
|
"kameleon_short_uuid" => kameleon_id.split("-").last,
|
27
26
|
"kameleon_cwd" => File.join(Kameleon.env.build_path, @name),
|
27
|
+
"in_context" => {"cmd"=> "/bin/bash"},
|
28
|
+
"out_context" => {"cmd"=> "/bin/bash"}
|
28
29
|
}
|
29
30
|
@aliases = {}
|
30
31
|
@checkpoint = nil
|
31
32
|
@files = []
|
32
33
|
@logger.debug("Initialize new recipe (#{path})")
|
34
|
+
@base_recipes_files = [@path]
|
33
35
|
load!
|
34
36
|
end
|
35
37
|
|
36
38
|
def load!
|
37
39
|
# Find recipe path
|
38
|
-
@logger.
|
40
|
+
@logger.debug("Loading #{@path}")
|
39
41
|
fail RecipeError, "Could not find this following recipe : #{@path}" \
|
40
42
|
unless File.file? @path
|
41
43
|
yaml_recipe = YAML.load File.open @path
|
42
44
|
unless yaml_recipe.kind_of? Hash
|
43
45
|
fail RecipeError, "Invalid yaml error"
|
44
46
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
# Load entended recipe variables
|
48
|
+
yaml_recipe = load_base_recipe(yaml_recipe)
|
49
|
+
yaml_recipe.delete("extend")
|
48
50
|
|
49
|
-
#Load Global variables
|
50
|
-
@global.merge!(yaml_recipe.fetch("global"))
|
51
|
+
# Load Global variables
|
52
|
+
@global.merge!(yaml_recipe.fetch("global", {}))
|
51
53
|
# Resolve dynamically-defined variables !!
|
52
54
|
resolved_global = Utils.resolve_vars(@global.to_yaml, @path, @global)
|
53
55
|
@global.merge! YAML.load(resolved_global)
|
@@ -73,7 +75,7 @@ module Kameleon
|
|
73
75
|
yaml_section = yaml_recipe.fetch(section.name)
|
74
76
|
next unless yaml_section.kind_of? Array
|
75
77
|
yaml_section.each do |raw_macrostep|
|
76
|
-
|
78
|
+
embedded_step = false
|
77
79
|
# Get macrostep name and arguments if available
|
78
80
|
if raw_macrostep.kind_of? String
|
79
81
|
name = raw_macrostep
|
@@ -85,13 +87,28 @@ module Kameleon
|
|
85
87
|
fail RecipeError, "Malformed yaml recipe in section: "\
|
86
88
|
"#{section.name}"
|
87
89
|
end
|
88
|
-
|
90
|
+
# Detect if step is embedded
|
91
|
+
if not args.nil?
|
92
|
+
args.each do |arg|
|
93
|
+
if arg.kind_of? Hash
|
94
|
+
if arg.flatten[1].kind_of? Array
|
95
|
+
embedded_step = true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
if embedded_step
|
101
|
+
@logger.debug("Loading embedded macrostep #{name}")
|
102
|
+
macrostep = load_macrostep(nil, name, args)
|
103
|
+
section.macrosteps.push(macrostep)
|
104
|
+
next
|
105
|
+
end
|
89
106
|
# Load macrostep yaml
|
90
107
|
loaded = false
|
91
108
|
dir_to_search.each do |dir|
|
92
109
|
macrostep_path = Pathname.new(File.join(dir, name + '.yaml'))
|
93
110
|
if File.file?(macrostep_path)
|
94
|
-
@logger.
|
111
|
+
@logger.debug("Loading macrostep #{macrostep_path}")
|
95
112
|
macrostep = load_macrostep(macrostep_path, name, args)
|
96
113
|
section.macrosteps.push(macrostep)
|
97
114
|
@files.push(macrostep_path)
|
@@ -108,28 +125,81 @@ module Kameleon
|
|
108
125
|
end
|
109
126
|
end
|
110
127
|
end
|
111
|
-
@logger.
|
128
|
+
@logger.debug("Loading recipe metadata")
|
112
129
|
@metainfo = {
|
113
|
-
"description" => Utils.extract_meta_var("description", @recipe_content)
|
114
|
-
"recipe" => Utils.extract_meta_var("recipe", @recipe_content),
|
115
|
-
"template" => Utils.extract_meta_var("template", @recipe_content),
|
130
|
+
"description" => Utils.extract_meta_var("description", @recipe_content)
|
116
131
|
}
|
117
132
|
end
|
118
133
|
|
134
|
+
def load_base_recipe(yaml_recipe)
|
135
|
+
base_recipe_name = yaml_recipe.fetch("extend", "")
|
136
|
+
return yaml_recipe if base_recipe_name.empty?
|
137
|
+
|
138
|
+
## check that the recipe has not already been loaded
|
139
|
+
base_recipe_name << ".yaml" unless base_recipe_name.end_with? ".yaml"
|
140
|
+
base_recipe_path = File.join(File.dirname(@path), base_recipe_name)
|
141
|
+
|
142
|
+
## check that the recipe has not already been loaded
|
143
|
+
return yaml_recipe if @base_recipes_files.include? base_recipe_path
|
144
|
+
|
145
|
+
base_recipe_path << ".yaml" unless base_recipe_path.end_with? ".yaml"
|
146
|
+
fail RecipeError, "Could not find this following recipe : #{@recipe_path}" \
|
147
|
+
unless File.file? @path
|
148
|
+
base_yaml_recipe = YAML.load File.open base_recipe_path
|
149
|
+
unless yaml_recipe.kind_of? Hash
|
150
|
+
fail RecipeError, "Invalid yaml error"
|
151
|
+
end
|
152
|
+
base_yaml_recipe.keys.each do |key|
|
153
|
+
if ["export", "bootstrap", "setup"].include? key
|
154
|
+
base_yaml_recipe.delete(key) unless yaml_recipe.keys.include? key
|
155
|
+
end
|
156
|
+
end
|
157
|
+
yaml_recipe.keys.each do |key|
|
158
|
+
if ["aliases", "checkpoint"].include? key
|
159
|
+
base_yaml_recipe[key] = yaml_recipe[key]
|
160
|
+
elsif ["export", "bootstrap", "setup"].include? key
|
161
|
+
base_section = base_yaml_recipe.fetch(key, [])
|
162
|
+
base_section = [] if base_section.nil?
|
163
|
+
recipe_section = yaml_recipe[key]
|
164
|
+
recipe_section = [] if recipe_section.nil?
|
165
|
+
index_base_steps = recipe_section.index("@base")
|
166
|
+
unless index_base_steps.nil?
|
167
|
+
recipe_section[index_base_steps] = base_section
|
168
|
+
recipe_section.flatten!
|
169
|
+
end
|
170
|
+
base_yaml_recipe[key] = recipe_section
|
171
|
+
elsif ["global"].include? key
|
172
|
+
base_section = base_yaml_recipe.fetch(key, {})
|
173
|
+
base_section = {} if base_section.nil?
|
174
|
+
recipe_section = yaml_recipe[key]
|
175
|
+
recipe_section = {} if recipe_section.nil?
|
176
|
+
base_yaml_recipe[key] = base_section.merge(recipe_section)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
@base_recipes_files.push(Pathname.new(base_recipe_path))
|
180
|
+
return load_base_recipe(base_yaml_recipe)
|
181
|
+
end
|
182
|
+
|
119
183
|
def load_aliases(yaml_recipe)
|
120
184
|
if yaml_recipe.keys.include? "aliases"
|
121
185
|
aliases = yaml_recipe.fetch("aliases")
|
122
186
|
if aliases.kind_of? Hash
|
123
187
|
@aliases = aliases
|
124
188
|
elsif aliases.kind_of? String
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
189
|
+
dir_search = [
|
190
|
+
File.join(File.dirname(@path), "steps", "aliases"),
|
191
|
+
File.join(File.dirname(@path), "aliases")
|
192
|
+
]
|
193
|
+
dir_search.each do |dir_path|
|
194
|
+
path = Pathname.new(File.join(dir_path, aliases))
|
195
|
+
if File.file?(path)
|
196
|
+
@logger.debug("Loading aliases #{path}")
|
197
|
+
@aliases = YAML.load_file(path)
|
198
|
+
@files.push(path)
|
199
|
+
return path
|
200
|
+
end
|
132
201
|
end
|
202
|
+
fail RecipeError, "Aliases file '#{path}' does not exists"
|
133
203
|
end
|
134
204
|
end
|
135
205
|
end
|
@@ -141,31 +211,39 @@ module Kameleon
|
|
141
211
|
@checkpoint = checkpoint
|
142
212
|
@checkpoint["path"] = @path
|
143
213
|
elsif checkpoint.kind_of? String
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
214
|
+
dir_search = [
|
215
|
+
File.join(File.dirname(@path), "steps", "checkpoints"),
|
216
|
+
File.join(File.dirname(@path), "checkpoints")
|
217
|
+
]
|
218
|
+
dir_search.each do |dir_path|
|
219
|
+
path = Pathname.new(File.join(dir_path, checkpoint))
|
220
|
+
if File.file?(path)
|
221
|
+
@logger.debug("Loading checkpoint configuration #{path}")
|
222
|
+
@checkpoint = YAML.load_file(path)
|
223
|
+
@checkpoint["path"] = path.to_s
|
224
|
+
@files.push(path)
|
225
|
+
return path
|
226
|
+
end
|
155
227
|
end
|
228
|
+
fail RecipeError, "Checkpoint configuraiton file '#{path}' " \
|
229
|
+
"does not exists"
|
156
230
|
end
|
157
231
|
end
|
158
232
|
end
|
159
233
|
|
160
234
|
def load_macrostep(step_path, name, args)
|
161
|
-
|
235
|
+
if step_path.nil?
|
236
|
+
macrostep_yaml = args
|
237
|
+
else
|
238
|
+
macrostep_yaml = YAML.load_file(step_path)
|
239
|
+
# Basic macrostep syntax check
|
240
|
+
if not macrostep_yaml.kind_of? Array
|
241
|
+
fail RecipeError, "The macrostep #{step_path} is not valid "
|
242
|
+
"(should be a list of microsteps)"
|
243
|
+
end
|
244
|
+
end
|
162
245
|
local_variables = {}
|
163
246
|
loaded_microsteps = []
|
164
|
-
# Basic macrostep syntax check
|
165
|
-
if not macrostep_yaml.kind_of? Array
|
166
|
-
fail RecipeError, "The macrostep #{step_path} is not valid "
|
167
|
-
"(should be a list of microsteps)"
|
168
|
-
end
|
169
247
|
# Load default local variables
|
170
248
|
macrostep_yaml.each do |yaml_microstep|
|
171
249
|
key = yaml_microstep.keys[0]
|
@@ -177,33 +255,35 @@ module Kameleon
|
|
177
255
|
local_variables[key] = @global.fetch(key, value)
|
178
256
|
end
|
179
257
|
end
|
180
|
-
|
181
|
-
|
182
|
-
args
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
258
|
+
unless step_path.nil?
|
259
|
+
selected_microsteps = []
|
260
|
+
if args
|
261
|
+
args.each do |entry|
|
262
|
+
if entry.kind_of? Hash
|
263
|
+
# resolve variable before using it
|
264
|
+
entry.each do |key, value|
|
265
|
+
local_variables[key] = value
|
266
|
+
end
|
267
|
+
elsif entry.kind_of? String
|
268
|
+
selected_microsteps.push entry
|
187
269
|
end
|
188
|
-
elsif entry.kind_of? String
|
189
|
-
selected_microsteps.push entry
|
190
270
|
end
|
191
271
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
272
|
+
unless selected_microsteps.empty?
|
273
|
+
# Some steps are selected so remove the others
|
274
|
+
# WARN: Allow the user to define this list not in the original order
|
275
|
+
strip_microsteps = []
|
276
|
+
selected_microsteps.each do |microstep_name|
|
277
|
+
macrostep = find_microstep(microstep_name, loaded_microsteps)
|
278
|
+
if macrostep.nil?
|
279
|
+
fail RecipeError, "Can't find microstep '#{microstep_name}' "\
|
280
|
+
"in macrostep file '#{step_path}'"
|
281
|
+
else
|
282
|
+
strip_microsteps.push(macrostep)
|
283
|
+
end
|
204
284
|
end
|
285
|
+
loaded_microsteps = strip_microsteps
|
205
286
|
end
|
206
|
-
loaded_microsteps = strip_microsteps
|
207
287
|
end
|
208
288
|
return Macrostep.new(name, loaded_microsteps, local_variables, step_path)
|
209
289
|
end
|
@@ -276,12 +356,6 @@ module Kameleon
|
|
276
356
|
end
|
277
357
|
end
|
278
358
|
@logger.notice("Starting recipe consistency check")
|
279
|
-
missings = []
|
280
|
-
@required_global.each do |key|
|
281
|
-
missings.push key unless @global.key? key
|
282
|
-
end
|
283
|
-
fail RecipeError, "Required parameters missing in global section :" \
|
284
|
-
" #{missings.join ' '}" unless missings.empty?
|
285
359
|
# check context args
|
286
360
|
required_args = %w(cmd)
|
287
361
|
missings = []
|
@@ -427,8 +501,8 @@ module Kameleon
|
|
427
501
|
"name" => @name,
|
428
502
|
"path" => @path.to_s,
|
429
503
|
"files" => @files.map {|p| p.to_s },
|
504
|
+
"base_recipes_files" => @base_recipes_files.map {|p| p.to_s },
|
430
505
|
"global" => @global,
|
431
|
-
"required_global" => @required_global,
|
432
506
|
"aliases" => @aliases,
|
433
507
|
}
|
434
508
|
recipe_hash["checkpoint"] = @checkpoint unless @checkpoint.nil?
|
@@ -448,26 +522,71 @@ module Kameleon
|
|
448
522
|
|
449
523
|
class RecipeTemplate < Recipe
|
450
524
|
|
451
|
-
def
|
525
|
+
def get_answer(msg)
|
526
|
+
while true
|
527
|
+
@logger.progress_notice msg
|
528
|
+
answer = $stdin.gets.downcase
|
529
|
+
raise AbortError, "Execution aborted..." if answer.nil?
|
530
|
+
answer.chomp!
|
531
|
+
if ["y", "n" , "", "a"].include?(answer)
|
532
|
+
if ["y", ""].include? answer
|
533
|
+
return true
|
534
|
+
elsif answer.eql? "a"
|
535
|
+
raise AbortError, "Aborted..."
|
536
|
+
end
|
537
|
+
return false
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
def safe_copy_file(src, dst, force)
|
543
|
+
if File.exists? dst
|
544
|
+
diff = Diffy::Diff.new(dst.to_s, src.to_s, :source => "files").to_s
|
545
|
+
unless diff.chomp.empty?
|
546
|
+
@logger.notice("conflict #{dst}")
|
547
|
+
@logger.notice("Differences between the old and the new :")
|
548
|
+
puts Diffy::Diff.new(dst.to_s, src.to_s,
|
549
|
+
:source => "files",
|
550
|
+
:context => 1,
|
551
|
+
:include_diff_info => true).to_s
|
552
|
+
msg = "Overwrite #{dst}? [Y]es/[n]o/[a]bort : "
|
553
|
+
if force || get_answer(msg)
|
554
|
+
FileUtils.copy_file(src, dst)
|
555
|
+
end
|
556
|
+
else
|
557
|
+
@logger.notice("identical #{dst}")
|
558
|
+
end
|
559
|
+
else
|
560
|
+
unless File.dirname(dst).eql? "/"
|
561
|
+
FileUtils.mkdir_p File.dirname(dst)
|
562
|
+
end
|
563
|
+
@logger.notice("create #{dst}")
|
564
|
+
FileUtils.copy_file(src, dst)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
def copy_extended_recipe(recipe_name, force)
|
452
569
|
Dir::mktmpdir do |tmp_dir|
|
453
570
|
recipe_path = File.join(tmp_dir, recipe_name + '.yaml')
|
454
|
-
|
571
|
+
## copying recipe
|
455
572
|
File.open(recipe_path, 'w+') do |file|
|
456
|
-
|
573
|
+
extend_erb_tpl = File.join(Kameleon.env.templates_path, "extend.erb")
|
574
|
+
tpl = ERB.new(File.open(extend_erb_tpl, 'rb') { |f| f.read })
|
457
575
|
result = tpl.result(binding)
|
458
576
|
file.write(result)
|
459
577
|
end
|
578
|
+
recipe_dst = File.join(Kameleon.env.workspace, recipe_name + '.yaml')
|
579
|
+
safe_copy_file(recipe_path, Pathname.new(recipe_dst), force)
|
580
|
+
end
|
581
|
+
end
|
460
582
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
# Create recipe dir if not exists
|
469
|
-
FileUtils.mkdir_p Kameleon.env.recipes_path
|
470
|
-
FileUtils.cp_r(Dir[tmp_dir + '/*'], Kameleon.env.recipes_path)
|
583
|
+
def copy_template(force)
|
584
|
+
## copying steps
|
585
|
+
files2copy = @base_recipes_files + @files
|
586
|
+
files2copy.each do |path|
|
587
|
+
relative_path = path.relative_path_from(Kameleon.env.templates_path)
|
588
|
+
dst = File.join(Kameleon.env.workspace, relative_path)
|
589
|
+
safe_copy_file(path, dst, force)
|
471
590
|
end
|
472
591
|
end
|
473
592
|
end
|