Class: ManifestV20071010

Inherits:
Object
  • Object
show all
Defined in:
lib/ec2/amitools/manifestv20071010.rb

Overview

Manifest Version 2007-10-10. Not backwards compatible

Defined Under Namespace

Classes: PartInformation

Constant Summary collapse

VERSION_STRING =
'2007-10-10'
VERSION =
VERSION_STRING.gsub('-','').to_i
IMAGE_TYPE_KERNEL =
"kernel"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(xml = nil) ⇒ ManifestV20071010

Returns a new instance of ManifestV20071010.



40
41
42
43
44
45
46
47
48
# 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

Class Method Details

.versionObject

Expose the version



25
26
27
# File 'lib/ec2/amitools/manifestv20071010.rb', line 25

def self.version
  VERSION
end

.version20071010?(xml) ⇒ Boolean

Returns:

  • (Boolean)


192
193
194
195
196
# 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

Instance Method Details

#ami_part_info_listObject

Retrieve a list of AMI bundle parts info. Each element is a hash with the following elements:

  • ‘digest’

  • ‘filename’

  • ‘index’



275
276
277
278
279
280
281
282
283
284
# 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_idsObject

Get the manifest’s ancestry



223
224
225
226
227
228
229
# 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

#archObject

Return the (optional) architecture of the AMI.



315
316
317
# File 'lib/ec2/amitools/manifestv20071010.rb', line 315

def arch()
  return get_element_text_or_nil('machine_configuration/architecture')
end

#authenticate(cert) ⇒ Object

Verify the signature



361
362
363
364
365
366
# 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_mappingObject

Get the block device mapping as a map.



299
300
301
302
303
304
305
306
307
# 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_sizeObject

Return the bundled size of the AMI.



320
321
322
# File 'lib/ec2/amitools/manifestv20071010.rb', line 320

def bundled_size()
  return get_element_text( 'image/bundled_size' ).to_i
end

#bundler_nameObject

Return the bundler name.



325
326
327
# File 'lib/ec2/amitools/manifestv20071010.rb', line 325

def bundler_name()
  return get_element_text('bundler/name')
end

#bundler_releaseObject

Return the bundler release.



335
336
337
# File 'lib/ec2/amitools/manifestv20071010.rb', line 335

def bundler_release()
  return get_element_text('bundler/release')
end

#bundler_versionObject

Return the bundler version.



330
331
332
# File 'lib/ec2/amitools/manifestv20071010.rb', line 330

def bundler_version()
  return get_element_text('bundler/version')
end

#cipher_algorithmObject

Get cipher algorithm used.



266
267
268
# 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

#digestObject

Return the AMI’s digest hex encoded.



232
233
234
# File 'lib/ec2/amitools/manifestv20071010.rb', line 232

def digest()
  return get_element_text( 'image/digest' )
end

#digest_algorithmObject

Get digest algorithm used.



261
262
263
# File 'lib/ec2/amitools/manifestv20071010.rb', line 261

def digest_algorithm()
  return REXML::XPath.first(@doc.root, 'image/digest/@algorithm').to_s
end

#docObject

for debugging only



51
52
53
# File 'lib/ec2/amitools/manifestv20071010.rb', line 51

def doc
  @doc
end

#ec2_encrypted_ivObject

The ec2 encrypted initialization vector hex encoded.



251
252
253
# File 'lib/ec2/amitools/manifestv20071010.rb', line 251

def ec2_encrypted_iv()
  return get_element_text( 'image/ec2_encrypted_iv' )
end

#ec2_encrypted_keyObject

The ec2 encrypted key hex encoded.



241
242
243
# File 'lib/ec2/amitools/manifestv20071010.rb', line 241

def ec2_encrypted_key()
  return get_element_text('image/ec2_encrypted_key' )
end

#image_typeObject



377
378
379
# File 'lib/ec2/amitools/manifestv20071010.rb', line 377

def image_type()
  return get_element_text('image/type')
end

#init(args) ⇒ Object

Initialize the manifest with AMI information. Return true if the initialization was succesful. Raise an exception on error.

Raises:

  • (ArgumentError)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# 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_idObject

Get the default kernel_id



204
205
206
# File 'lib/ec2/amitools/manifestv20071010.rb', line 204

def kernel_id()
  return get_element_text_or_nil('machine_configuration/kernel_id')
end

#kernel_nameObject

Get the kernel_name



199
200
201
# 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) ⇒ Object



55
56
57
58
# 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

#nameObject



236
237
238
# File 'lib/ec2/amitools/manifestv20071010.rb', line 236

def name()
  return get_element_text( 'image/name' )
end

#optional_argument(arg, args) ⇒ Object



60
61
62
# File 'lib/ec2/amitools/manifestv20071010.rb', line 60

def optional_argument(arg, args)
  args[arg]
end

#partsObject

A list of PartInformation instances representing the AMI parts.



287
288
289
290
291
292
293
294
295
296
# 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_codesObject

Get the default product codes



214
215
216
217
218
219
220
# 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_idObject

Get the default ramdisk id



209
210
211
# 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) ⇒ Object

Sign the manifest. If it is already signed, the signature and certificate will be replaced



341
342
343
344
345
346
347
348
349
350
351
352
353
# 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

#signatureObject

Return the signature



356
357
358
# File 'lib/ec2/amitools/manifestv20071010.rb', line 356

def signature
  get_element_text('signature')
end

#sizeObject

Return the size of the AMI.



310
311
312
# File 'lib/ec2/amitools/manifestv20071010.rb', line 310

def size()
  return get_element_text( 'image/size' ).to_i()
end

#to_sObject

Return the manifest as an XML string.



369
370
371
# File 'lib/ec2/amitools/manifestv20071010.rb', line 369

def to_s()
  return @doc.to_s
end

#userObject



373
374
375
# File 'lib/ec2/amitools/manifestv20071010.rb', line 373

def user()
  return get_element_text('image/user')
end

#user_encrypted_ivObject

The user encrypted initialization vector hex encoded.



256
257
258
# File 'lib/ec2/amitools/manifestv20071010.rb', line 256

def user_encrypted_iv()
  return get_element_text( 'image/user_encrypted_iv' )
end

#user_encrypted_keyObject

The user encrypted key hex encoded.



246
247
248
# File 'lib/ec2/amitools/manifestv20071010.rb', line 246

def user_encrypted_key()
  return get_element_text( 'image/user_encrypted_key' )
end

#versionObject



381
382
383
# File 'lib/ec2/amitools/manifestv20071010.rb', line 381

def version()
  return get_element_text('version').gsub('-','').to_i
end