class Bootscript::Script
Main functional class. Models and builds a self-extracting Bash/TAR file.
Attributes
A Hash of data sources to be written onto the boot target’s filesystem. Each (String) key is a path to the desired file on the boot target. Each value can be a String (treated as ERB), or Object with a read method. Any Ruby File objects with extension “.ERB” are also processed as ERB.
Standard Ruby Logger, overridden by passing :logger to {#initialize}
Public Class Methods
constructor - configures the AWS S3 connection and logging @param logger [::Logger] - a standard Ruby logger
# File lib/bootscript/script.rb, line 25 def initialize(logger = nil) @log ||= logger || Bootscript.default_logger @data_map = Hash.new @vars = Hash.new end
Public Instance Methods
Generates the BootScript contents by interpreting the @data_map based on erb_vars. If destination has a write() method, the data is streamed there line-by-line, and the number of bytes written is returned. Otherwise, the BootScript contents are returned as a String. In the case of streaming output, the destination must be already opened. @param erb_vars [Hash] Ruby variables to interpolate into all templates @param destination [IO] a Ruby object that responds to write(String) @return [Fixnum] the number of bytes written to the destination, or @return [String] the text of the rendered script, if destination is nil
# File lib/bootscript/script.rb, line 40 def generate(erb_vars = {}, destination = nil) # Set state / instance variables, used by publish() and helper methods @vars = Bootscript.merge_platform_defaults(erb_vars) output = destination || StringIO.open(@script_data = "") @bytes_written = 0 if Bootscript.windows?(@vars) @bytes_written += output.write(render_erb_text(File.read( "#{File.dirname(__FILE__)}/../templates/windows_header.bat.erb" ))) end write_bootscript(output) # streams the script part line-by-line write_uuencoded_archive(output) # streams the archive line-by-line if Bootscript.windows?(@vars) @bytes_written += output.write(render_erb_text(File.read( "#{File.dirname(__FILE__)}/../templates/windows_footer.bat.erb" ))) end output.close unless destination # (close StringIO if it was opened) return (destination ? @bytes_written : @script_data) end
Private Instance Methods
merges the @data_map with the Chef
built-ins, as-needed
# File lib/bootscript/script.rb, line 158 def full_data_map Bootscript::Chef.included?(@vars) ? @data_map.merge(Bootscript::Chef.files(@vars)) : @data_map end
renders each data map item into an ‘archive’, which must be either an Archive::Tar::Minitar::Writer (if unix), or a Zip::OutputStream (windows)
# File lib/bootscript/script.rb, line 124 def render_data_map_into(archive) full_data_map.each do |remote_path, item| if item.is_a? String # case 1: data item is a String @log.debug "Rendering ERB data (#{item[0..16]}...) into archive" data = render_erb_text(item) input = StringIO.open(data, 'r') size = data.bytes.count elsif item.is_a?(File) # case 2: data item is an ERB file if item.path.upcase.sub(/\A.*\./,'') == 'ERB' @log.debug "Rendering ERB file #{item.path} into archive" data = render_erb_text(item.read) input = StringIO.open(data, 'r') size = data.bytes.count else # case 3: data item is a regular File @log.debug "Copying data from #{item.inspect} into archive" input = item size = File.stat(item).size end else # case 4: Error raise ArgumentError.new("cannot process item: #{item}") end if Bootscript.windows?(@vars) archive.put_next_entry remote_path archive.write input.read else opts = {mode: 0600, size: size, mtime: Time.now} archive.add_file_simple(remote_path, opts) do |output| while data = input.read(512) ; output.write data end end end end end
renders erb_text, using @vars
# File lib/bootscript/script.rb, line 164 def render_erb_text(erb_text) Erubis::Eruby.new(erb_text).result(@vars) end
strips all empty lines and lines beginning with # from text does NOT touch the first line of text
# File lib/bootscript/script.rb, line 170 def strip_shell_comments(text) lines = text.lines.to_a return text if lines.count < 2 lines.first + lines[1..lines.count]. reject{|l| (l =~ /^\s*#/) || (l =~ /^\s+$/)}.join('') end
Streams the bootscript to destination and updates @bytes_written
# File lib/bootscript/script.rb, line 64 def write_bootscript(destination) # If streaming, send the top-level script line-by-line from memory if Bootscript.windows?(@vars) template_path = Bootscript::WINDOWS_TEMPLATE else template_path = Bootscript::UNIX_TEMPLATE end template = File.read(template_path) template = strip_shell_comments(template) if @vars[:strip_comments] @log.debug "Rendering boot script to #{destination}..." render_erb_text(template).each_line do |ln| destination.write ln @bytes_written += ln.bytes.count end end
Streams a uuencoded TGZ archive to destination, updating @bytes_written
# File lib/bootscript/script.rb, line 95 def write_unix_archive(destination) begin uuencode = UUWriter.new(destination) gz = Zlib::GzipWriter.new(uuencode) tar = Archive::Tar::Minitar::Writer.open(gz) render_data_map_into(tar) ensure tar.close gz.close @bytes_written += uuencode.bytes_written end end
Streams the uuencoded archive to destination, updating @bytes_written
# File lib/bootscript/script.rb, line 81 def write_uuencoded_archive(destination) @log.debug "Writing #{@vars[:platform]} archive to #{destination}..." if Bootscript.windows?(@vars) @bytes_written += destination.write("$archive = @'\n") write_windows_archive(destination) @bytes_written += destination.write("'@\n") else # :platform = 'unix' @bytes_written += destination.write("begin-base64 0600 bootstrap.tbz\n") write_unix_archive(destination) @bytes_written += destination.write("====\n") # (base64 footer) end end
Streams a uuencoded ZIP archive to destination, updating @bytes_written
# File lib/bootscript/script.rb, line 109 def write_windows_archive(destination) Dir.mktmpdir do |dir| zip_path = "#{dir}/archive.zip" zipfile = File.open(zip_path, 'wb') Zip::OutputStream.open(zipfile) {|zip| render_data_map_into(zip)} zipfile.close @log.debug "zipfile = #{zip_path}, length = #{File.size zip_path}" File.open(zip_path, 'rb') do |zipfile| @bytes_written += destination.write([zipfile.read].pack 'm') end end end