class MANIFEST_CLASS
Manifest Version 2007-10-10. Not backwards compatible
Manifest Version 2007-10-10. Not backwards compatible
Constants
- IMAGE_TYPE_KERNEL
- VERSION
- VERSION_STRING
Public Class Methods
# File lib/ec2/amitools/manifestv20071010.rb, line 40 def initialize( xml = nil ) if xml == nil @doc = REXML::Document.new else # Convert to string if necessary. xml = ( xml.kind_of?( IO ) ? xml.read : xml ) @doc = REXML::Document.new( xml ) end end
Expose the version
# File lib/ec2/amitools/manifestv20071010.rb, line 25 def self.version VERSION end
# File lib/ec2/amitools/manifestv20071010.rb, line 192 def self::version20071010?(xml) doc = REXML::Document.new(xml) version = REXML::XPath.first(doc.root, 'version') return (version and version.text and version.text == VERSION_STRING) end
Public Instance Methods
Retrieve a list of AMI bundle parts info. Each element is a hash with the following elements:
-
'digest'
-
'filename'
-
'index'
# File lib/ec2/amitools/manifestv20071010.rb, line 275 def ami_part_info_list parts = Array.new REXML::XPath.each( @doc.root,'image/parts/part' ) do |part| index = part.attribute( 'index' ).to_s.to_i filename = REXML::XPath.first( part, 'filename' ).text digest = REXML::XPath.first( part, 'digest' ).text parts << { 'digest'=>digest, 'filename'=>filename, 'index'=>index } end return parts end
Get the manifest's ancestry
# File lib/ec2/amitools/manifestv20071010.rb, line 223 def ancestor_ami_ids() ancestor_ami_ids = [] REXML::XPath.each(@doc, '/manifest/image/ancestry/ancestor_ami_id') do |node| ancestor_ami_ids << node.text unless (node.text.nil? or node.text.empty?) end ancestor_ami_ids end
Return the (optional) architecture of the AMI.
# File lib/ec2/amitools/manifestv20071010.rb, line 315 def arch() return get_element_text_or_nil('machine_configuration/architecture') end
Verify the signature
# File lib/ec2/amitools/manifestv20071010.rb, line 361 def authenticate(cert) machine_configuration_xml = XMLUtil.get_xml( @doc.to_s, 'machine_configuration' ) || "" image_xml = XMLUtil.get_xml( @doc.to_s, 'image' ) pubkey = Crypto::cert2pubkey(cert) Crypto::authenticate(machine_configuration_xml + image_xml, Format::hex2bin(signature), pubkey) end
Get the block device mapping as a map.
# File lib/ec2/amitools/manifestv20071010.rb, line 299 def block_device_mapping() bdm = {} REXML::XPath.each(@doc.root,'machine_configuration/block_device_mapping/mapping/') do |mapping| virtual = REXML::XPath.first(mapping, 'virtual').text device = REXML::XPath.first(mapping, 'device').text bdm[virtual] = device end bdm end
Return the bundled size of the AMI.
# File lib/ec2/amitools/manifestv20071010.rb, line 320 def bundled_size() return get_element_text( 'image/bundled_size' ).to_i end
Return the bundler name.
# File lib/ec2/amitools/manifestv20071010.rb, line 325 def bundler_name() return get_element_text('bundler/name') end
Return the bundler release.
# File lib/ec2/amitools/manifestv20071010.rb, line 335 def bundler_release() return get_element_text('bundler/release') end
Return the bundler version.
# File lib/ec2/amitools/manifestv20071010.rb, line 330 def bundler_version() return get_element_text('bundler/version') end
Get cipher algorithm used.
# File lib/ec2/amitools/manifestv20071010.rb, line 266 def cipher_algorithm() return REXML::XPath.first(@doc.root, 'image/ec2_encrypted_key/@algorithm').to_s end
Return the AMI's digest hex encoded.
# File lib/ec2/amitools/manifestv20071010.rb, line 232 def digest() return get_element_text( 'image/digest' ) end
Get digest algorithm used.
# File lib/ec2/amitools/manifestv20071010.rb, line 261 def digest_algorithm() return REXML::XPath.first(@doc.root, 'image/digest/@algorithm').to_s end
for debugging only
# File lib/ec2/amitools/manifestv20071010.rb, line 51 def doc @doc end
The ec2 encrypted initialization vector hex encoded.
# File lib/ec2/amitools/manifestv20071010.rb, line 251 def ec2_encrypted_iv() return get_element_text( 'image/ec2_encrypted_iv' ) end
The ec2 encrypted key hex encoded.
# File lib/ec2/amitools/manifestv20071010.rb, line 241 def ec2_encrypted_key() return get_element_text('image/ec2_encrypted_key' ) end
# File lib/ec2/amitools/manifestv20071010.rb, line 377 def image_type() return get_element_text('image/type') end
Initialize the manifest with AMI information. Return true
if the initialization was succesful. Raise an exception on error.
# File lib/ec2/amitools/manifestv20071010.rb, line 69 def init(args) name = mandatory_argument(:name, args) user = mandatory_argument(:user, args) # The user's account number. arch = mandatory_argument(:arch, args) # Target architecture for AMI. image_type = mandatory_argument(:image_type, args) # Type of image reserved = mandatory_argument(:reserved, args) # Reserved for future use; pass nil. parts = mandatory_argument(:parts, args) # A list of parts filenames and digest pairs. size = mandatory_argument(:size, args) # The size of the AMI in bytes. bundled_size = mandatory_argument(:bundled_size, args) # The size of the bunled AMI in bytes. user_encrypted_key = mandatory_argument(:user_encrypted_key, args) # Hex encoded. ec2_encrypted_key = mandatory_argument(:ec2_encrypted_key, args) # Hex encoded. cipher_algorithm = mandatory_argument(:cipher_algorithm, args) # The cipher algorithm used to encrypted the AMI. user_encrypted_iv = mandatory_argument(:user_encrypted_iv, args) # Hex encoded. ec2_encrypted_iv = mandatory_argument(:ec2_encrypted_iv, args) # Hex encoded. digest = mandatory_argument(:digest, args) # Hex encoded. digest_algorithm = mandatory_argument(:digest_algorithm, args) # The digest algorithm. privkey_filename = mandatory_argument(:privkey_filename, args) # The user's private key filename. # Optional parameters kernel_id = optional_argument(:kernel_id, args) # Optional default kernel image id ramdisk_id = optional_argument(:ramdisk_id, args) # Optional default ramdisk image id product_codes = optional_argument(:product_codes, args) # Optional array of product codes (strings) ancestor_ami_ids = optional_argument(:ancestor_ami_ids, args) # Optional array of ancestor ami ids (strings) bdm = optional_argument(:block_device_mapping, args) ||{} # Optional hash of block device mappings(strings) bundler_name = optional_argument(:bundler_name, args) bundler_version = optional_argument(:bundler_version, args) bundler_release = optional_argument(:bundler_release, args) # Conditional parameters kernel_name = (image_type == IMAGE_TYPE_KERNEL ? mandatory_argument(:kernel_name, args) : nil) # Name of the kernel in the image # Check reserved parameters are nil raise ArgumentError.new( "reserved parameters not nil" ) unless reserved.nil? # Check non-String parameter types. raise ArgumentError.new( "parts parameter type invalid" ) unless parts.is_a? Array # XML document. @doc = REXML::Document.new @doc << REXML::XMLDecl.new # Yeah... the way we used to do this really sucked - manually building up REXML::Element and inserting them into # parent nodes. So I've reinvented the wheel and done a baby xpath xml builder kinda thing # Makes it much easier (from a code point of view) to build up xml docs. Probably less efficient in machine terms but c'mon # if we cared about that we wouldn't be using ruby. builder = XMLBuilder.new(@doc) # version - indicate the manifest version. builder['/manifest/version'] = VERSION_STRING # bundler information builder['/manifest/bundler/name'] = bundler_name builder['/manifest/bundler/version'] = bundler_version builder['/manifest/bundler/release'] = bundler_release # machine_configuration - the target hardware description of the AMI. builder['/manifest/machine_configuration/architecture'] = arch bdm.keys.sort.each_with_index do |key, index| builder["/manifest/machine_configuration/block_device_mapping/mapping[#{index}]/virtual"] = key builder["/manifest/machine_configuration/block_device_mapping/mapping[#{index}]/device"] = bdm[key] end builder['/manifest/machine_configuration/kernel_id'] = kernel_id builder['/manifest/machine_configuration/ramdisk_id'] = ramdisk_id Array(product_codes).each_with_index do |product_code, index| builder["/manifest/machine_configuration/product_codes/product_code[#{index}]"] = product_code end # image - the image element. builder['manifest/image/name'] = name # user - the user's AWS access key ID. builder['/manifest/image/user'] = user builder['/manifest/image/type'] = image_type # The name of the kernel in the image. Only applicable to kernel images. builder['/manifest/image/kernel_name'] = kernel_name # ancestry - the parent ami ids (ancestor_ami_ids || []).each_with_index do |ancestor_ami_id, index| builder["/manifest/image/ancestry/ancestor_ami_id[#{index}]"] = ancestor_ami_id end # digest - the digest of the AMI. builder['/manifest/image/digest'] = digest builder['/manifest/image/digest/@algorithm'] = digest_algorithm # size - the size of the uncompressed AMI. builder['/manifest/image/size'] = size.to_s # bundled size - the size of the bundled AMI. builder['/manifest/image/bundled_size'] = bundled_size.to_s # ec2 encrypted key element. builder['/manifest/image/ec2_encrypted_key'] = ec2_encrypted_key builder['/manifest/image/ec2_encrypted_key/@algorithm'] = cipher_algorithm # user encrypted key element. builder['/manifest/image/user_encrypted_key'] = user_encrypted_key builder['/manifest/image/user_encrypted_key/@algorithm'] = cipher_algorithm # ec2 encrypted iv element. builder['/manifest/image/ec2_encrypted_iv'] = ec2_encrypted_iv # user encrypted iv element. builder['/manifest/image/user_encrypted_iv'] = user_encrypted_iv # parts - list of the image parts. builder['/manifest/image/parts/@count'] = parts.size parts.each_with_index do |part, index| # Add image part element for each image part. builder["/manifest/image/parts/part[#{index}]/@index"] = index builder["/manifest/image/parts/part[#{index}]/filename"] = part[0] builder["/manifest/image/parts/part[#{index}]/digest"] = Format::bin2hex(part[1]) builder["/manifest/image/parts/part[#{index}]/digest/@algorithm"] = digest_algorithm builder["/manifest/image/parts/part[#{index}]/@index"] = index end # Sign the manifest. sign(privkey_filename) return true end
Get the default kernel_id
# File lib/ec2/amitools/manifestv20071010.rb, line 204 def kernel_id() return get_element_text_or_nil('machine_configuration/kernel_id') end
Get the kernel_name
# File lib/ec2/amitools/manifestv20071010.rb, line 199 def kernel_name() return get_element_text_or_nil('image/kernel_name') end
# File lib/ec2/amitools/manifestv20071010.rb, line 55 def mandatory_argument(arg, args) raise "Missing mandatory argument #{arg} for manifest #{VERSION_STRING}" if !args.key?(arg) args[arg] end
# File lib/ec2/amitools/manifestv20071010.rb, line 236 def name() return get_element_text( 'image/name' ) end
# File lib/ec2/amitools/manifestv20071010.rb, line 60 def optional_argument(arg, args) args[arg] end
A list of PartInformation
instances representing the AMI parts.
# File lib/ec2/amitools/manifestv20071010.rb, line 287 def parts() parts = [] REXML::XPath.each( @doc.root,'image/parts/part' ) do |part| index = part.attribute( 'index' ).to_s.to_i filename = REXML::XPath.first( part, 'filename' ).text digest = Format::hex2bin( REXML::XPath.first( part, 'digest' ).text ) parts[index] = PartInformation.new( filename, digest ) end return parts end
Get the default product codes
# File lib/ec2/amitools/manifestv20071010.rb, line 214 def product_codes() product_codes = [] REXML::XPath.each(@doc, '/manifest/machine_configuration/product_codes/product_code') do |product_code| product_codes << product_code.text end product_codes end
Get the default ramdisk id
# File lib/ec2/amitools/manifestv20071010.rb, line 209 def ramdisk_id() return get_element_text_or_nil('machine_configuration/ramdisk_id') end
Sign the manifest. If it is already signed, the signature and certificate will be replaced
# File lib/ec2/amitools/manifestv20071010.rb, line 341 def sign( privkey_filename ) unless privkey_filename.kind_of? String and File::exist?( privkey_filename ) raise ArgumentError.new( "privkey_filename parameter invalid" ) end # Get the XML for <machine_configuration> and <image> elements and sign them. machine_configuration_xml = XMLUtil.get_xml( @doc.to_s, 'machine_configuration' ) || "" image_xml = XMLUtil.get_xml( @doc.to_s, 'image' ) sig = Crypto::sign( machine_configuration_xml + image_xml, privkey_filename ) # Create the signature and certificate elements. XMLBuilder.new(@doc)['/manifest/signature'] = Format::bin2hex(sig) end
Return the signature
# File lib/ec2/amitools/manifestv20071010.rb, line 356 def signature get_element_text('signature') end
Return the size of the AMI.
# File lib/ec2/amitools/manifestv20071010.rb, line 310 def size() return get_element_text( 'image/size' ).to_i() end
Return the manifest as an XML string.
# File lib/ec2/amitools/manifestv20071010.rb, line 369 def to_s() return @doc.to_s end
# File lib/ec2/amitools/manifestv20071010.rb, line 373 def user() return get_element_text('image/user') end
The user encrypted initialization vector hex encoded.
# File lib/ec2/amitools/manifestv20071010.rb, line 256 def user_encrypted_iv() return get_element_text( 'image/user_encrypted_iv' ) end
The user encrypted key hex encoded.
# File lib/ec2/amitools/manifestv20071010.rb, line 246 def user_encrypted_key() return get_element_text( 'image/user_encrypted_key' ) end
# File lib/ec2/amitools/manifestv20071010.rb, line 381 def version() return get_element_text('version').gsub('-','').to_i end
Private Instance Methods
# File lib/ec2/amitools/manifestv20071010.rb, line 393 def get_element_text(xpath) element = REXML::XPath.first(@doc.root, xpath) unless element raise "invalid AMI manifest, #{xpath} element not present" end unless element.text raise "invalid AMI manifest, #{xpath} element empty" end return element.text end
# File lib/ec2/amitools/manifestv20071010.rb, line 387 def get_element_text_or_nil(xpath) element = REXML::XPath.first(@doc.root, xpath) return element.text if element return nil end