racker 0.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.
@@ -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