class ManifestV20071010

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

new( xml = nil ) click to toggle source
# 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
version() click to toggle source

Expose the version

# File lib/ec2/amitools/manifestv20071010.rb, line 25
def self.version
  VERSION
end
version20071010?(xml) click to toggle source
# 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

ami_part_info_list() click to toggle source

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
ancestor_ami_ids() click to toggle source

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
arch() click to toggle source

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
authenticate(cert) click to toggle source

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
block_device_mapping() click to toggle source

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
bundled_size() click to toggle source

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
bundler_name() click to toggle source

Return the bundler name.

# File lib/ec2/amitools/manifestv20071010.rb, line 325
def bundler_name()
  return get_element_text('bundler/name')
end
bundler_release() click to toggle source

Return the bundler release.

# File lib/ec2/amitools/manifestv20071010.rb, line 335
def bundler_release()
  return get_element_text('bundler/release')
end
bundler_version() click to toggle source

Return the bundler version.

# File lib/ec2/amitools/manifestv20071010.rb, line 330
def bundler_version()
  return get_element_text('bundler/version')
end
cipher_algorithm() click to toggle source

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
digest() click to toggle source

Return the AMI's digest hex encoded.

# File lib/ec2/amitools/manifestv20071010.rb, line 232
def digest()
  return get_element_text( 'image/digest' )
end
digest_algorithm() click to toggle source

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
doc() click to toggle source

for debugging only

# File lib/ec2/amitools/manifestv20071010.rb, line 51
def doc
  @doc
end
ec2_encrypted_iv() click to toggle source

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
ec2_encrypted_key() click to toggle source

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
image_type() click to toggle source
# File lib/ec2/amitools/manifestv20071010.rb, line 377
def image_type()
  return get_element_text('image/type')
end
init(args) click to toggle source

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
kernel_id() click to toggle source

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
kernel_name() click to toggle source

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
mandatory_argument(arg, args) click to toggle source
# 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
name() click to toggle source
# File lib/ec2/amitools/manifestv20071010.rb, line 236
def name()
  return get_element_text( 'image/name' )
end
optional_argument(arg, args) click to toggle source
# File lib/ec2/amitools/manifestv20071010.rb, line 60
def optional_argument(arg, args)
  args[arg]
end
parts() click to toggle source

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
product_codes() click to toggle source

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
ramdisk_id() click to toggle source

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( privkey_filename ) click to toggle source

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
signature() click to toggle source

Return the signature

# File lib/ec2/amitools/manifestv20071010.rb, line 356
def signature
  get_element_text('signature')
end
size() click to toggle source

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
to_s() click to toggle source

Return the manifest as an XML string.

# File lib/ec2/amitools/manifestv20071010.rb, line 369
def to_s()
  return @doc.to_s
end
user() click to toggle source
# File lib/ec2/amitools/manifestv20071010.rb, line 373
def user()
  return get_element_text('image/user')
end
user_encrypted_iv() click to toggle source

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
user_encrypted_key() click to toggle source

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
version() click to toggle source
# File lib/ec2/amitools/manifestv20071010.rb, line 381
def version()
  return get_element_text('version').gsub('-','').to_i
end

Private Instance Methods

get_element_text(xpath) click to toggle source
# 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
get_element_text_or_nil(xpath) click to toggle source
# 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