Class: ManifestV20070829

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

Overview

Manifest Version 2007-08-29. Not backwards compatible

Defined Under Namespace

Classes: PartInformation

Constant Summary collapse

VERSION_STRING =
'2007-08-29'
VERSION =
VERSION_STRING.gsub('-','_').to_i

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(xml = nil) ⇒ ManifestV20070829



39
40
41
42
43
44
45
46
47
# File 'lib/ec2/amitools/manifestv20070829.rb', line 39

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



24
25
26
# File 'lib/ec2/amitools/manifestv20070829.rb', line 24

def self.version
  VERSION
end

.version20070829?(xml) ⇒ Boolean



200
201
202
203
204
205
# File 'lib/ec2/amitools/manifestv20070829.rb', line 200

def self::version20070829?(xml)
  doc = REXML::Document.new(xml)
  version = REXML::XPath.first(doc.root, 'version')
  return true if (version and version.text and version.text == VERSION_STRING)
  return (version and version.text and version.text == VERSION.to_s)
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’



251
252
253
254
255
256
257
258
259
260
# File 'lib/ec2/amitools/manifestv20070829.rb', line 251

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

#archObject

Return the (optional) architecture of the AMI.



280
281
282
# File 'lib/ec2/amitools/manifestv20070829.rb', line 280

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

#authenticate(cert) ⇒ Object

Verify the signature



329
330
331
332
333
334
# File 'lib/ec2/amitools/manifestv20070829.rb', line 329

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

#bundled_sizeObject

Return the bundled size of the AMI.



285
286
287
# File 'lib/ec2/amitools/manifestv20070829.rb', line 285

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

#bundler_nameObject

Return the bundler name.



290
291
292
# File 'lib/ec2/amitools/manifestv20070829.rb', line 290

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

#bundler_releaseObject

Return the bundler release.



300
301
302
# File 'lib/ec2/amitools/manifestv20070829.rb', line 300

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

#bundler_versionObject

Return the bundler version.



295
296
297
# File 'lib/ec2/amitools/manifestv20070829.rb', line 295

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

#cipher_algorithmObject

Get cipher algorithm used.



242
243
244
# File 'lib/ec2/amitools/manifestv20070829.rb', line 242

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.



208
209
210
# File 'lib/ec2/amitools/manifestv20070829.rb', line 208

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

#digest_algorithmObject

Get digest algorithm used.



237
238
239
# File 'lib/ec2/amitools/manifestv20070829.rb', line 237

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

#docObject

for debugging only



50
51
52
# File 'lib/ec2/amitools/manifestv20070829.rb', line 50

def doc
  @doc
end

#ec2_encrypted_ivObject

The ec2 encrypted initialization vector hex encoded.



227
228
229
# File 'lib/ec2/amitools/manifestv20070829.rb', line 227

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

#ec2_encrypted_keyObject

The ec2 encrypted key hex encoded.



217
218
219
# File 'lib/ec2/amitools/manifestv20070829.rb', line 217

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

#init(name, user, arch, reserved, parts, size, bundled_size, user_encrypted_key, ec2_encrypted_key, cipher_algorithm, user_encrypted_iv, ec2_encrypted_iv, digest, digest_algorithm, privkey_filename, bundler_name = nil, bundler_version = nil, bundler_release = nil) ⇒ Object

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

Raises:

  • (ArgumentError)


57
58
59
60
61
62
63
64
65
66
67
68
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
191
192
193
194
195
196
197
198
# File 'lib/ec2/amitools/manifestv20070829.rb', line 57

def init(
  name,
  user,                   # The user's account number.
  arch,                   # Target architecture for AMI.
  reserved,               # Reserved for future use; pass nil.
  parts,                  # A list of parts filenames and digest pairs.
  size,                   # The size of the AMI in bytes.
  bundled_size,           # The size of the bunled AMI in bytes.
  user_encrypted_key,     # Hex encoded.
  ec2_encrypted_key,      # Hex encoded.
  cipher_algorithm,       # The cipher algorithm used to encrypted the AMI.
  user_encrypted_iv,      # Hex encoded.
  ec2_encrypted_iv,       # Hex encoded.
  digest,                 # Hex encoded.
  digest_algorithm,       # The digest algorithm.
  privkey_filename,       # The user's private key filename.
  bundler_name = nil,
  bundler_version = nil,
  bundler_release = nil)

  # 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

  # manifest - the root element.
  manifest = REXML::Element.new('manifest')

  @doc.add_element(manifest)
  
  # version - indicate the manifest version.
  version = REXML::Element.new('version')
  version.text = VERSION
  manifest.add_element(version)
  
  # bundler information
  if bundler_name or bundler_version or bundler_release
    bundler_element = REXML::Element.new('bundler')
    manifest.add_element(bundler_element)
    
    [['name', bundler_name ], 
     ['version', bundler_version ],
     ['release', bundler_release ]].each do |element_name, text|
      if element_name
        element = REXML::Element.new(element_name)
        element.text = text
        bundler_element.add_element(element)
      end
    end 
  end
  
  # machine_configuration - the target hardware description of the AMI.
  machine_configuration = REXML::Element.new('machine_configuration')
  manifest.add_element(machine_configuration)

  # image - the image element.
  image = REXML::Element.new('image')
  name_element = REXML::Element.new('name')
  name_element.text = name
  image.add_element(name_element)
  manifest.add_element(image)
  
  # user - the user's AWS access key ID.
  user_element = REXML::Element.new('user')
  user_element.text = user
  image.add_element(user_element)

  # m_c arch - the target hardware architecture
  if arch
    arch_element = REXML::Element.new('architecture')
    arch_element.text = arch
    machine_configuration.add_element(arch_element)
  end

  # digest - the digest of the AMI.
  digest_element = REXML::Element.new('digest')
  digest_element.add_attribute('algorithm', digest_algorithm)
  digest_element.add_text(digest)
  image.add_element(digest_element)
  
  # size - the size of the uncompressed AMI.
  size_element = REXML::Element.new('size')
  size_element.text = size.to_s
  image.add_element(size_element)
  
  # bundled size - the size of the bundled AMI.
  bundled_size_element = REXML::Element.new('bundled_size')
  bundled_size_element.text = bundled_size.to_s
  image.add_element(bundled_size_element)
  
  # ec2 encrypted key element.
  ec2_encrypted_key_element = REXML::Element.new('ec2_encrypted_key')
  ec2_encrypted_key_element.add_attribute('algorithm', cipher_algorithm)
  ec2_encrypted_key_element.add_text(ec2_encrypted_key)
  image.add_element(ec2_encrypted_key_element)
  
  # user encrypted key element.
  user_encrypted_key_element = REXML::Element.new('user_encrypted_key')
  user_encrypted_key_element.add_attribute('algorithm', cipher_algorithm)
  user_encrypted_key_element.add_text(user_encrypted_key)
  image.add_element(user_encrypted_key_element)
  
  # ec2 encrypted iv element.
  ec2_encrypted_iv_element = REXML::Element.new('ec2_encrypted_iv')
  ec2_encrypted_iv_element.add_text(ec2_encrypted_iv)
  image.add_element(ec2_encrypted_iv_element)
  
  # user encrypted iv element.
  user_encrypted_iv_element = REXML::Element.new('user_encrypted_iv')
  user_encrypted_iv_element.add_text(user_encrypted_iv)
  image.add_element(user_encrypted_iv_element)
  
  # parts - list of the image parts.
  parts_element = REXML::Element.new('parts')
  parts_element.add_attributes({'count' => parts.size.to_s})
  index=0
  parts.each do |part|
    # Add image part element for each image part.
    part_element = REXML::Element.new('part')
    part_element.add_attribute('index', index.to_s)
    filename = REXML::Element.new('filename')
    filename.add_text(part[0])
    part_element.add_element(filename)
    digest = REXML::Element.new('digest')
    digest.add_attribute('algorithm', digest_algorithm)
    digest.add_text(Format::bin2hex(part[1]))
    part_element.add_element(digest)
    parts_element.add_element(part_element)
    index+=1
  end
  image.add_element(parts_element)

  # Sign the manifest.
  sign(privkey_filename)
  
  return true
end

#nameObject



212
213
214
# File 'lib/ec2/amitools/manifestv20070829.rb', line 212

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

#partsObject

A list of PartInformation instances representing the AMI parts.



263
264
265
266
267
268
269
270
271
272
# File 'lib/ec2/amitools/manifestv20070829.rb', line 263

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

#sign(privkey_filename) ⇒ Object

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



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/ec2/amitools/manifestv20070829.rb', line 306

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.
  signature = REXML::Element.new('signature')
  signature.add_text(Format::bin2hex(sig))
  @doc.root.delete_element('signature')
  @doc.root.add_element(signature)
end

#signatureObject

Return the signature



324
325
326
# File 'lib/ec2/amitools/manifestv20070829.rb', line 324

def signature
  get_element_text('signature')
end

#sizeObject

Return the size of the AMI.



275
276
277
# File 'lib/ec2/amitools/manifestv20070829.rb', line 275

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

#to_sObject

Return the manifest as an XML string.



337
338
339
# File 'lib/ec2/amitools/manifestv20070829.rb', line 337

def to_s()
  return @doc.to_s
end

#userObject



341
342
343
# File 'lib/ec2/amitools/manifestv20070829.rb', line 341

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

#user_encrypted_ivObject

The user encrypted initialization vector hex encoded.



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

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

#user_encrypted_keyObject

The user encrypted key hex encoded.



222
223
224
# File 'lib/ec2/amitools/manifestv20070829.rb', line 222

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

#versionObject



345
346
347
# File 'lib/ec2/amitools/manifestv20070829.rb', line 345

def version()
  return get_element_text('version').to_i
end