racker 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require 'racker/builders/builder'
3
+
4
+ module Racker
5
+ module Builders
6
+ # This is the Google builder
7
+ class Google < Racker::Builders::Builder
8
+ def to_packer(name, config)
9
+ log = Log4r::Logger['racker']
10
+ log.debug("Entering #{self.class}.#{__method__}")
11
+ config = super(name, config)
12
+
13
+ # There are no special cases at this point
14
+
15
+ # %w().each do |key|
16
+ # if config.key? key
17
+ # log.info("Converting #{key} to packer value...")
18
+ # config[key] = convert_hash_to_packer_value(config[key])
19
+ # end
20
+ # end
21
+
22
+ log.debug("Leaving #{self.class}.#{__method__}")
23
+ config
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require 'racker/builders/builder'
3
+
4
+ module Racker
5
+ module Builders
6
+ # This is the OpenStack builder
7
+ class OpenStack < Racker::Builders::Builder
8
+ def to_packer(name, config)
9
+ log = Log4r::Logger['racker']
10
+ log.debug("Entering #{self.class}.#{__method__}")
11
+ config = super(name, config)
12
+
13
+ # There are no special cases at this point
14
+
15
+ # %w().each do |key|
16
+ # if config.key? key
17
+ # log.info("Converting #{key} to packer value...")
18
+ # config[key] = convert_hash_to_packer_value(config[key])
19
+ # end
20
+ # end
21
+
22
+ log.debug("Leaving #{self.class}.#{__method__}")
23
+ config
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ require 'racker/builders/builder'
3
+
4
+ module Racker
5
+ module Builders
6
+ # This is the QEMU builder
7
+ class QEMU < Racker::Builders::Builder
8
+ def to_packer(name, config)
9
+ log = Log4r::Logger['racker']
10
+ log.debug("Entering #{self.class}.#{__method__}")
11
+ config = super(name, config)
12
+
13
+ %w(boot_command floppy_files iso_urls qemuargs).each do |key|
14
+ if config.key? key
15
+ log.info("Converting #{key} to packer value...")
16
+ config[key] = convert_hash_to_packer_value(config[key])
17
+ end
18
+ end
19
+
20
+ log.debug("Leaving #{self.class}.#{__method__}")
21
+ config
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ require 'racker/builders/builder'
3
+
4
+ module Racker
5
+ module Builders
6
+ # This is the Virtualbox builder
7
+ class Virtualbox < Racker::Builders::Builder
8
+ def to_packer(name, config)
9
+ log = Log4r::Logger['racker']
10
+ log.debug("Entering #{self.class}.#{__method__}")
11
+ config = super(name, config)
12
+
13
+ %w(boot_command floppy_files iso_urls vboxmanage).each do |key|
14
+ if config.key? key
15
+ log.info("Converting #{key} to packer value...")
16
+ config[key] = convert_hash_to_packer_value(config[key])
17
+ end
18
+ end
19
+
20
+ log.debug("Leaving #{self.class}.#{__method__}")
21
+ config
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ require 'racker/builders/builder'
3
+
4
+ module Racker
5
+ module Builders
6
+ # This is the VMware builder
7
+ class VMware < Racker::Builders::Builder
8
+ def to_packer(name, config)
9
+ log = Log4r::Logger['racker']
10
+ log.debug("Entering #{self.class}.#{__method__}")
11
+ config = super(name, config)
12
+
13
+ %w(boot_command floppy_files iso_urls vboxmanage).each do |key|
14
+ if config.key? key
15
+ log.info("Converting #{key} to packer value...")
16
+ config[key] = convert_hash_to_packer_value(config[key])
17
+ end
18
+ end
19
+
20
+ log.debug("Leaving #{self.class}.#{__method__}")
21
+ config
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/racker/cli.rb ADDED
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+ require 'optparse'
3
+ require 'racker/processor'
4
+ require 'racker/version'
5
+ require 'log4r'
6
+
7
+ module Racker
8
+ # The CLI is a class responsible for handling the command line interface
9
+ # logic.
10
+ class CLI
11
+ attr_reader :options
12
+
13
+ def initialize(argv)
14
+ @argv = argv
15
+ end
16
+
17
+ def execute!
18
+ # Get the global logger
19
+ log = Log4r::Logger['racker']
20
+
21
+ # Parse our arguments
22
+ option_parser.parse!(@argv)
23
+
24
+ # Set the logging level specified by the command line
25
+ log.level = get_log4r_level(options[:log_level])
26
+ log.info("Log level set to: #{options[:log_level]}")
27
+
28
+ # Display the options if a minimum of 1 template and an output file is not provided
29
+ if @argv.length < 2
30
+ puts option_parser
31
+ Kernel.exit!(1)
32
+ end
33
+
34
+ # Set the output file to the last arg
35
+ options[:output] = @argv.pop
36
+ log.debug("Output file set to: #{options[:output]}")
37
+
38
+ # Set the input files to the remaining args
39
+ options[:templates] = @argv
40
+
41
+ # Run through Racker
42
+ log.debug('Executing the Racker Processor...')
43
+ Processor.new(options).execute!
44
+ log.debug('Processing complete.')
45
+
46
+ # Thats all folks!
47
+ puts "Processing complete! Packer file generated: #{options[:output]}"
48
+
49
+ return 0
50
+ end
51
+
52
+ private
53
+
54
+ def get_log4r_level(level)
55
+ case level
56
+ when /fatal/
57
+ Log4r::FATAL
58
+ when /error/
59
+ Log4r::ERROR
60
+ when /warn/
61
+ Log4r::WARN
62
+ when /info/
63
+ Log4r::INFO
64
+ when /debug/
65
+ Log4r::DEBUG
66
+ else
67
+ Log4r::INFO
68
+ end
69
+ end
70
+
71
+ def options
72
+ @options ||= {
73
+ log_level: :warn,
74
+ output: '',
75
+ templates: [],
76
+ }
77
+ end
78
+
79
+ def option_parser
80
+ @option_parser ||= OptionParser.new do |opts|
81
+ opts.banner = "Usage: #{opts.program_name} [options] [TEMPLATE1, TEMPLATE2, ...] OUTPUT"
82
+
83
+ opts.on('-l', '--log-level [LEVEL]', [:fatal, :error, :warn, :info, :debug], 'Set log level') do |v|
84
+ options[:log_level] = v
85
+ end
86
+
87
+ opts.on_tail('-h', '--help', 'Show this message') do
88
+ puts option_parser
89
+ Kernel.exit!(0)
90
+ end
91
+
92
+ opts.on_tail('-v', '--version', "Show #{opts.program_name} version") do
93
+ puts Racker::Version.version
94
+ Kernel.exit!(0)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+
3
+ require 'fileutils'
4
+ require 'json'
5
+ require 'racker/template'
6
+ require 'pp'
7
+
8
+ module Racker
9
+ # This class handles command line options.
10
+ class Processor
11
+
12
+ CONFIGURE_MUTEX = Mutex.new
13
+
14
+ def initialize(options)
15
+ @options = options
16
+ end
17
+
18
+ def execute!
19
+ # Get the global logger
20
+ log = Log4r::Logger['racker']
21
+
22
+ # Verify that the templates exist
23
+ @options[:templates].each do |template|
24
+ raise "File does not exist! (#{template})" unless ::File.exists?(template)
25
+ end
26
+
27
+ # Check that the output directory exists
28
+ output_dir = File.dirname(File.expand_path(@options[:output]))
29
+
30
+ # If the output directory doesnt exist
31
+ log.info('Creating the output directory if it does not exist...')
32
+ FileUtils.mkdir_p output_dir unless File.exists? output_dir
33
+
34
+ # Load the templates
35
+ templates = []
36
+
37
+ # Load the template procs
38
+ log.info('Loading racker templates...')
39
+ template_procs = load(@options[:templates])
40
+
41
+ # Load the actual templates
42
+ log.info('Processing racker templates...')
43
+ template_procs.each do |version,proc|
44
+ # Create the new template
45
+ template = Racker::Template.new
46
+
47
+ # Run the block with the template
48
+ proc.call(template)
49
+
50
+ # Store the template
51
+ templates << template
52
+ end
53
+ log.info('Racker template processing complete.')
54
+
55
+ # Get the first template and merge each subsequent one on the latest
56
+ log.info('Merging racker templates...')
57
+ current_template = templates.shift
58
+
59
+ # Overlay the templates
60
+ templates.each do |template|
61
+ current_template = current_template.deep_merge!(template, {:knockout_prefix => '--'})
62
+ end
63
+
64
+ # Compact the residual template to remove nils
65
+ log.info('Compacting racker template...')
66
+ compact_template = current_template.compact(:recurse => true)
67
+
68
+ # Write the compact template out to file
69
+ File.open(@options[:output], 'w') do |file|
70
+ log.info('Writing packer template...')
71
+ file.write(JSON.pretty_generate(compact_template.to_packer))
72
+ log.info('Writing packer template complete.')
73
+ end
74
+ end
75
+
76
+ def load(templates)
77
+ return capture_templates do
78
+ templates.each do |template|
79
+ puts "Loading template file: #{template}"
80
+ Kernel.load template
81
+ end
82
+ end
83
+ end
84
+
85
+ # This is a class method so the templates can load it
86
+ def self.register_template(version='1',&block)
87
+ @@last_procs ||= []
88
+ @@last_procs << [version, block]
89
+ end
90
+
91
+ def capture_templates
92
+ CONFIGURE_MUTEX.synchronize do
93
+ @@last_procs = []
94
+
95
+ yield
96
+
97
+ return @@last_procs
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,210 @@
1
+ module DeepMergeModified
2
+
3
+ class InvalidParameter < StandardError; end
4
+
5
+ DEFAULT_FIELD_KNOCKOUT_PREFIX = '--'
6
+
7
+ # Deep Merge core documentation.
8
+ # deep_merge! method permits merging of arbitrary child elements. The two top level
9
+ # elements must be hashes. These hashes can contain unlimited (to stack limit) levels
10
+ # of child elements. These child elements to not have to be of the same types.
11
+ # Where child elements are of the same type, deep_merge will attempt to merge them together.
12
+ # Where child elements are not of the same type, deep_merge will skip or optionally overwrite
13
+ # the destination element with the contents of the source element at that level.
14
+ # So if you have two hashes like this:
15
+ # source = {:x => [1,2,3], :y => 2}
16
+ # dest = {:x => [4,5,'6'], :y => [7,8,9]}
17
+ # dest.deep_merge!(source)
18
+ # Results: {:x => [1,2,3,4,5,'6'], :y => 2}
19
+ # By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
20
+ # To avoid this, use "deep_merge" (no bang/exclamation mark)
21
+ #
22
+ # Options:
23
+ # Options are specified in the last parameter passed, which should be in hash format:
24
+ # hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
25
+ # :preserve_unmergeables DEFAULT: false
26
+ # Set to true to skip any unmergeable elements from source
27
+ # :knockout_prefix DEFAULT: nil
28
+ # Set to string value to signify prefix which deletes elements from existing element
29
+ # :sort_merged_arrays DEFAULT: false
30
+ # Set to true to sort all arrays that are merged together
31
+ # :unpack_arrays DEFAULT: nil
32
+ # Set to string value to run "Array::join" then "String::split" against all arrays
33
+ # :merge_hash_arrays DEFAULT: false
34
+ # Set to true to merge hashes within arrays
35
+ # :merge_debug DEFAULT: false
36
+ # Set to true to get console output of merge process for debugging
37
+ #
38
+ # Selected Options Details:
39
+ # :knockout_prefix => The purpose of this is to provide a way to remove elements
40
+ # from existing Hash by specifying them in a special way in incoming hash
41
+ # source = {:x => ['--1', '2']}
42
+ # dest = {:x => ['1', '3']}
43
+ # dest.ko_deep_merge!(source)
44
+ # Results: {:x => ['2','3']}
45
+ # Additionally, if the knockout_prefix is passed alone as a string, it will cause
46
+ # the entire element to be removed:
47
+ # source = {:x => '--'}
48
+ # dest = {:x => [1,2,3]}
49
+ # dest.ko_deep_merge!(source)
50
+ # Results: {:x => ""}
51
+ # :unpack_arrays => The purpose of this is to permit compound elements to be passed
52
+ # in as strings and to be converted into discrete array elements
53
+ # irsource = {:x => ['1,2,3', '4']}
54
+ # dest = {:x => ['5','6','7,8']}
55
+ # dest.deep_merge!(source, {:unpack_arrays => ','})
56
+ # Results: {:x => ['1','2','3','4','5','6','7','8'}
57
+ # Why: If receiving data from an HTML form, this makes it easy for a checkbox
58
+ # to pass multiple values from within a single HTML element
59
+ #
60
+ # :merge_hash_arrays => merge hashes within arrays
61
+ # source = {:x => [{:y => 1}]}
62
+ # dest = {:x => [{:z => 2}]}
63
+ # dest.deep_merge!(source, {:merge_hash_arrays => true})
64
+ # Results: {:x => [{:y => 1, :z => 2}]}
65
+ #
66
+ # There are many tests for this library - and you can learn more about the features
67
+ # and usages of deep_merge! by just browsing the test examples
68
+ def self.deep_merge!(source, dest, options = {})
69
+ # turn on this line for stdout debugging text
70
+ merge_debug = options[:merge_debug] || false
71
+ overwrite_unmergeable = !options[:preserve_unmergeables]
72
+ knockout_prefix = options[:knockout_prefix] || nil
73
+ raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!" if knockout_prefix == ""
74
+ raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!" if knockout_prefix && !overwrite_unmergeable
75
+ # if present: we will split and join arrays on this char before merging
76
+ array_split_char = options[:unpack_arrays] || false
77
+ # request that we sort together any arrays when they are merged
78
+ sort_merged_arrays = options[:sort_merged_arrays] || false
79
+ # request that arrays of hashes are merged together
80
+ merge_hash_arrays = options[:merge_hash_arrays] || false
81
+ di = options[:debug_indent] || ''
82
+ # do nothing if source is nil
83
+ return dest if source.nil?
84
+ # if dest doesn't exist, then simply copy source to it
85
+ if !(dest) && overwrite_unmergeable
86
+ dest = source; return dest
87
+ end
88
+
89
+ puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
90
+ if source.kind_of?(Hash)
91
+ puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
92
+ source.each do |src_key, src_value|
93
+ if dest.kind_of?(Hash)
94
+ puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
95
+ if dest[src_key]
96
+ puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
97
+ dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
98
+ else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
99
+ puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
100
+ # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
101
+ begin
102
+ src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
103
+ rescue TypeError
104
+ src_dup = src_value
105
+ end
106
+ dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
107
+ end
108
+ else # dest isn't a hash, so we overwrite it completely (if permitted)
109
+ if overwrite_unmergeable
110
+ puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
111
+ dest = overwrite_unmergeables(source, dest, options)
112
+ end
113
+ end
114
+ end
115
+ elsif source.kind_of?(Array)
116
+ puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
117
+ # if we are instructed, join/split any source arrays before processing
118
+ if array_split_char
119
+ puts "#{di} split/join on source: #{source.inspect}" if merge_debug
120
+ source = source.join(array_split_char).split(array_split_char)
121
+ if dest.kind_of?(Array)
122
+ dest = dest.join(array_split_char).split(array_split_char)
123
+ end
124
+ end
125
+ # if there's a naked knockout_prefix in source, that means we are to truncate dest
126
+ if source.index(knockout_prefix)
127
+ dest = clear_or_nil(dest); source.delete(knockout_prefix)
128
+ end
129
+ if dest.kind_of?(Array)
130
+ if knockout_prefix
131
+ print "#{di} knocking out: " if merge_debug
132
+ # remove knockout prefix items from both source and dest
133
+ source.delete_if do |ko_item|
134
+ retval = false
135
+ item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
136
+ if item != ko_item
137
+ print "#{ko_item} - " if merge_debug
138
+ dest.delete(item)
139
+ dest.delete(ko_item)
140
+ retval = true
141
+ end
142
+ retval
143
+ end
144
+ puts if merge_debug
145
+ end
146
+ puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
147
+ source_all_hashes = source.all? { |i| i.kind_of?(Hash) }
148
+ dest_all_hashes = dest.all? { |i| i.kind_of?(Hash) }
149
+ if merge_hash_arrays && source_all_hashes && dest_all_hashes
150
+ # merge hashes in lists
151
+ list = []
152
+ dest.each_index do |i|
153
+ list[i] = deep_merge!(source[i] || {}, dest[i],
154
+ options.merge(:debug_indent => di + ' '))
155
+ end
156
+ list += source[dest.count..-1] if source.count > dest.count
157
+ dest = list
158
+ else
159
+ dest = dest | source
160
+ end
161
+ dest.sort! if sort_merged_arrays
162
+ elsif overwrite_unmergeable
163
+ puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
164
+ dest = overwrite_unmergeables(source, dest, options)
165
+ end
166
+ else # src_hash is not an array or hash, so we'll have to overwrite dest
167
+ puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
168
+ dest = overwrite_unmergeables(source, dest, options)
169
+ end
170
+ puts "#{di}Returning #{dest.inspect}" if merge_debug
171
+ dest
172
+ end # deep_merge!
173
+
174
+ # allows deep_merge! to uniformly handle overwriting of unmergeable entities
175
+ def self.overwrite_unmergeables(source, dest, options)
176
+ merge_debug = options[:merge_debug] || false
177
+ overwrite_unmergeable = !options[:preserve_unmergeables]
178
+ knockout_prefix = options[:knockout_prefix] || false
179
+ di = options[:debug_indent] || ''
180
+ if knockout_prefix && overwrite_unmergeable
181
+ if source.kind_of?(String) # remove knockout string from source before overwriting dest
182
+ src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
183
+ elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
184
+ src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
185
+ else
186
+ src_tmp = source
187
+ end
188
+ if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
189
+ puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
190
+ dest = src_tmp
191
+ else # if we do find a knockout_prefix, then we just delete dest
192
+ puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
193
+ dest = nil
194
+ end
195
+ elsif overwrite_unmergeable
196
+ dest = source
197
+ end
198
+ dest
199
+ end
200
+
201
+ def self.clear_or_nil(obj)
202
+ if obj.respond_to?(:clear)
203
+ obj.clear
204
+ else
205
+ obj = nil
206
+ end
207
+ obj
208
+ end
209
+
210
+ end # module DeepMergeModified