ec2_amitools 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +54 -0
  3. data/bin/console +14 -0
  4. data/bin/ec2-ami-tools-version +6 -0
  5. data/bin/ec2-bundle-image +6 -0
  6. data/bin/ec2-bundle-vol +6 -0
  7. data/bin/ec2-delete-bundle +6 -0
  8. data/bin/ec2-download-bundle +6 -0
  9. data/bin/ec2-migrate-bundle +6 -0
  10. data/bin/ec2-migrate-manifest +6 -0
  11. data/bin/ec2-unbundle +6 -0
  12. data/bin/ec2-upload-bundle +6 -0
  13. data/bin/setup +8 -0
  14. data/etc/ec2/amitools/cert-ec2-cn-north-1.pem +28 -0
  15. data/etc/ec2/amitools/cert-ec2-gov.pem +17 -0
  16. data/etc/ec2/amitools/cert-ec2.pem +23 -0
  17. data/etc/ec2/amitools/mappings.csv +9 -0
  18. data/lib/ec2/amitools/bundle.rb +251 -0
  19. data/lib/ec2/amitools/bundle_base.rb +58 -0
  20. data/lib/ec2/amitools/bundleimage.rb +94 -0
  21. data/lib/ec2/amitools/bundleimageparameters.rb +42 -0
  22. data/lib/ec2/amitools/bundlemachineparameters.rb +60 -0
  23. data/lib/ec2/amitools/bundleparameters.rb +120 -0
  24. data/lib/ec2/amitools/bundlevol.rb +240 -0
  25. data/lib/ec2/amitools/bundlevolparameters.rb +164 -0
  26. data/lib/ec2/amitools/crypto.rb +379 -0
  27. data/lib/ec2/amitools/decryptmanifest.rb +20 -0
  28. data/lib/ec2/amitools/defaults.rb +12 -0
  29. data/lib/ec2/amitools/deletebundle.rb +212 -0
  30. data/lib/ec2/amitools/deletebundleparameters.rb +78 -0
  31. data/lib/ec2/amitools/downloadbundle.rb +161 -0
  32. data/lib/ec2/amitools/downloadbundleparameters.rb +84 -0
  33. data/lib/ec2/amitools/exception.rb +86 -0
  34. data/lib/ec2/amitools/fileutil.rb +219 -0
  35. data/lib/ec2/amitools/format.rb +127 -0
  36. data/lib/ec2/amitools/instance-data.rb +97 -0
  37. data/lib/ec2/amitools/manifest_wrapper.rb +132 -0
  38. data/lib/ec2/amitools/manifestv20070829.rb +361 -0
  39. data/lib/ec2/amitools/manifestv20071010.rb +403 -0
  40. data/lib/ec2/amitools/manifestv3.rb +331 -0
  41. data/lib/ec2/amitools/mapids.rb +148 -0
  42. data/lib/ec2/amitools/migratebundle.rb +222 -0
  43. data/lib/ec2/amitools/migratebundleparameters.rb +173 -0
  44. data/lib/ec2/amitools/migratemanifest.rb +225 -0
  45. data/lib/ec2/amitools/migratemanifestparameters.rb +118 -0
  46. data/lib/ec2/amitools/minimalec2.rb +116 -0
  47. data/lib/ec2/amitools/parameter_exceptions.rb +34 -0
  48. data/lib/ec2/amitools/parameters_base.rb +168 -0
  49. data/lib/ec2/amitools/region.rb +93 -0
  50. data/lib/ec2/amitools/s3toolparameters.rb +183 -0
  51. data/lib/ec2/amitools/showversion.rb +12 -0
  52. data/lib/ec2/amitools/syschecks.rb +27 -0
  53. data/lib/ec2/amitools/tool_base.rb +224 -0
  54. data/lib/ec2/amitools/unbundle.rb +107 -0
  55. data/lib/ec2/amitools/unbundleparameters.rb +65 -0
  56. data/lib/ec2/amitools/uploadbundle.rb +361 -0
  57. data/lib/ec2/amitools/uploadbundleparameters.rb +108 -0
  58. data/lib/ec2/amitools/util.rb +532 -0
  59. data/lib/ec2/amitools/version.rb +33 -0
  60. data/lib/ec2/amitools/xmlbuilder.rb +237 -0
  61. data/lib/ec2/amitools/xmlutil.rb +55 -0
  62. data/lib/ec2/common/constants.rb +16 -0
  63. data/lib/ec2/common/curl.rb +110 -0
  64. data/lib/ec2/common/headers.rb +95 -0
  65. data/lib/ec2/common/headersv4.rb +173 -0
  66. data/lib/ec2/common/http.rb +333 -0
  67. data/lib/ec2/common/s3support.rb +231 -0
  68. data/lib/ec2/common/signature.rb +68 -0
  69. data/lib/ec2/oem/LICENSE.txt +58 -0
  70. data/lib/ec2/oem/open4.rb +399 -0
  71. data/lib/ec2/platform/base/architecture.rb +26 -0
  72. data/lib/ec2/platform/base/constants.rb +54 -0
  73. data/lib/ec2/platform/base/pipeline.rb +181 -0
  74. data/lib/ec2/platform/base.rb +57 -0
  75. data/lib/ec2/platform/current.rb +55 -0
  76. data/lib/ec2/platform/linux/architecture.rb +35 -0
  77. data/lib/ec2/platform/linux/constants.rb +23 -0
  78. data/lib/ec2/platform/linux/fstab.rb +99 -0
  79. data/lib/ec2/platform/linux/identity.rb +16 -0
  80. data/lib/ec2/platform/linux/image.rb +811 -0
  81. data/lib/ec2/platform/linux/mtab.rb +74 -0
  82. data/lib/ec2/platform/linux/pipeline.rb +40 -0
  83. data/lib/ec2/platform/linux/rsync.rb +114 -0
  84. data/lib/ec2/platform/linux/tar.rb +124 -0
  85. data/lib/ec2/platform/linux/uname.rb +50 -0
  86. data/lib/ec2/platform/linux.rb +83 -0
  87. data/lib/ec2/platform/solaris/architecture.rb +28 -0
  88. data/lib/ec2/platform/solaris/constants.rb +30 -0
  89. data/lib/ec2/platform/solaris/fstab.rb +43 -0
  90. data/lib/ec2/platform/solaris/identity.rb +16 -0
  91. data/lib/ec2/platform/solaris/image.rb +327 -0
  92. data/lib/ec2/platform/solaris/mtab.rb +29 -0
  93. data/lib/ec2/platform/solaris/pipeline.rb +40 -0
  94. data/lib/ec2/platform/solaris/rsync.rb +24 -0
  95. data/lib/ec2/platform/solaris/tar.rb +36 -0
  96. data/lib/ec2/platform/solaris/uname.rb +21 -0
  97. data/lib/ec2/platform/solaris.rb +38 -0
  98. data/lib/ec2/platform.rb +69 -0
  99. data/lib/ec2/version.rb +8 -0
  100. data/lib/ec2_amitools +1 -0
  101. data/lib/ec2_amitools.rb +7 -0
  102. metadata +184 -0
@@ -0,0 +1,97 @@
1
+ # Copyright 2008-2014 Amazon.com, Inc. or its affiliates. All Rights
2
+ # Reserved. Licensed under the Amazon Software License (the
3
+ # "License"). You may not use this file except in compliance with the
4
+ # License. A copy of the License is located at
5
+ # http://aws.amazon.com/asl or in the "license" file accompanying this
6
+ # file. This file is distributed on an "AS IS" BASIS, WITHOUT
7
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
8
+ # the License for the specific language governing permissions and
9
+ # limitations under the License.
10
+
11
+ require 'open-uri'
12
+
13
+ module EC2
14
+ class InstanceData
15
+
16
+ META_DATA_URL = "http://169.254.169.254/latest/meta-data/"
17
+
18
+ attr_reader :instance_data_accessible
19
+
20
+ def initialize(meta_data_url = META_DATA_URL)
21
+ @meta_data_url = meta_data_url
22
+ # see if we can access the meta data. Be unforgiving - if anything goes wrong
23
+ # just mark instance data as unaccessible.
24
+ begin
25
+ open(@meta_data_url)
26
+ @instance_data_accessible = true
27
+ rescue StandardError => e
28
+ @instance_data_accessible = false
29
+ end
30
+ end
31
+
32
+ def kernel_id
33
+ read_meta_data('kernel-id')
34
+ end
35
+
36
+ def ramdisk_id
37
+ read_meta_data('ramdisk-id')
38
+ end
39
+
40
+ def ami_id
41
+ read_meta_data('ami-id')
42
+ end
43
+
44
+ def ancestor_ami_ids
45
+ read_meta_data_list('ancestor-ami-ids')
46
+ end
47
+
48
+ def product_codes
49
+ read_meta_data_list('product-codes')
50
+ end
51
+
52
+ def block_device_mapping
53
+ read_meta_data_hash('block-device-mapping')
54
+ end
55
+
56
+ def availability_zone
57
+ read_meta_data('placement/availability-zone')
58
+ end
59
+
60
+ def read_meta_data_hash(path)
61
+ keys = list_meta_data_index(path)
62
+ return nil if keys.nil?
63
+ hash = {}
64
+ keys.each do |key|
65
+ value = read_meta_data(File.join(path, key))
66
+ hash[key] = value if value
67
+ end
68
+ hash
69
+ end
70
+ private :read_meta_data_hash
71
+
72
+ def read_meta_data_list(path)
73
+ list = read_meta_data(path)
74
+ list.nil? ? nil : list.split("\n")
75
+ end
76
+ private :read_meta_data_list
77
+
78
+ def list_meta_data_index(path)
79
+ read_meta_data_list(File.join(path, ''))
80
+ end
81
+ private :list_meta_data_index
82
+
83
+ def read_meta_data(path)
84
+ nil if !@instance_data_accessible
85
+ begin
86
+ open(File.join(@meta_data_url, path)) do |cio|
87
+ return cio.read.to_s.strip
88
+ end
89
+ rescue
90
+ return nil
91
+ end
92
+ end
93
+ private :read_meta_data
94
+
95
+ end
96
+ end
97
+
@@ -0,0 +1,132 @@
1
+ # Copyright 2008-2014 Amazon.com, Inc. or its affiliates. All Rights
2
+ # Reserved. Licensed under the Amazon Software License (the
3
+ # "License"). You may not use this file except in compliance with the
4
+ # License. A copy of the License is located at
5
+ # http://aws.amazon.com/asl or in the "license" file accompanying this
6
+ # file. This file is distributed on an "AS IS" BASIS, WITHOUT
7
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
8
+ # the License for the specific language governing permissions and
9
+ # limitations under the License.
10
+
11
+ require 'ec2/amitools/manifestv20071010'
12
+ require 'ec2/amitools/manifestv20070829'
13
+ require 'ec2/amitools/manifestv3'
14
+ require 'rexml/document'
15
+
16
+ class ManifestWrapper
17
+
18
+ class InvalidManifest < RuntimeError
19
+ end
20
+
21
+ # All the manifest fields we support.
22
+ V3_FIELDS = [
23
+ :name,
24
+ :user,
25
+ :parts,
26
+ :size,
27
+ :bundled_size,
28
+ :user_encrypted_key,
29
+ :ec2_encrypted_key,
30
+ :cipher_algorithm,
31
+ :user_encrypted_iv,
32
+ :ec2_encrypted_iv,
33
+ :digest,
34
+ :digest_algorithm,
35
+ :bundler_name,
36
+ :bundler_version,
37
+ :bundler_release,
38
+ ]
39
+
40
+ V3_FIELDS.each { |field| attr_reader(field) }
41
+
42
+ V20070829_FIELDS = [
43
+ :arch,
44
+ ]
45
+
46
+ V20070829_FIELDS.each { |field| attr_reader(field) }
47
+
48
+ V20071010_FIELDS = [
49
+ :image_type,
50
+ :kernel_id,
51
+ :ramdisk_id,
52
+ :product_codes,
53
+ :ancestor_ami_ids,
54
+ :block_device_mapping,
55
+ :kernel_name,
56
+ ]
57
+
58
+ V20071010_FIELDS.each { |field| attr_reader(field) }
59
+
60
+ # We want to pass some methods through as well.
61
+ METHODS = [
62
+ :authenticate,
63
+ ]
64
+
65
+ METHODS.each do |method|
66
+ define_method(method) do |*args|
67
+ @manifest.send(method, *args)
68
+ end
69
+ end
70
+
71
+ # Should the caller want the underlying manifest for some reason.
72
+ attr_reader :manifest
73
+
74
+ def get_manifest_version(manifest_xml)
75
+ begin
76
+ version_elem = REXML::XPath.first(REXML::Document.new(manifest_xml), '/manifest/version')
77
+ raise InvalidManifest.new("Invalid manifest.") if version_elem.nil?
78
+ return version_elem.text.to_i
79
+ rescue => e
80
+ raise InvalidManifest.new("Invalid manifest.")
81
+ end
82
+ end
83
+
84
+ def initialize(manifest_xml)
85
+ version = get_manifest_version(manifest_xml)
86
+
87
+ if version > 20071010
88
+ raise InvalidManifest.new("Manifest is too new for this tool to handle. Please upgrade.")
89
+ end
90
+
91
+ if version < 3
92
+ raise InvalidManifest.new("Manifest is too old for this tool to handle.")
93
+ end
94
+
95
+ # Try figure out what manifest version we have
96
+ @manifest = if ManifestV20071010::version20071010?(manifest_xml)
97
+ ManifestV20071010.new(manifest_xml)
98
+ elsif ManifestV20070829::version20070829?(manifest_xml)
99
+ ManifestV20070829.new(manifest_xml)
100
+ elsif ManifestV3::version3?(manifest_xml)
101
+ ManifestV3.new(manifest_xml)
102
+ else
103
+ raise InvalidManifest.new("Unrecognised manifest version.")
104
+ end
105
+
106
+ # Now populate the fields. First, stuff that's in all the
107
+ # manifests we deal with.
108
+ V3_FIELDS.each do |field|
109
+ instance_variable_set("@#{field.to_s}", @manifest.send(field))
110
+ end
111
+
112
+ # Next, the next version up.
113
+ if @manifest.version > 3
114
+ V20070829_FIELDS.each do |field|
115
+ instance_variable_set("@#{field.to_s}", @manifest.send(field))
116
+ end
117
+ else
118
+ # Some mandatory fields we need in later versions:
119
+ @arch = 'i386'
120
+ end
121
+
122
+ # Next, the next version up.
123
+ if @manifest.version > 20070829
124
+ V20071010_FIELDS.each do |field|
125
+ instance_variable_set("@#{field.to_s}", @manifest.send(field))
126
+ end
127
+ else
128
+ # Some mandatory fields we need in later versions:
129
+ @image_type = 'machine'
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,361 @@
1
+ # Copyright 2008-2014 Amazon.com, Inc. or its affiliates. All Rights
2
+ # Reserved. Licensed under the Amazon Software License (the
3
+ # "License"). You may not use this file except in compliance with the
4
+ # License. A copy of the License is located at
5
+ # http://aws.amazon.com/asl or in the "license" file accompanying this
6
+ # file. This file is distributed on an "AS IS" BASIS, WITHOUT
7
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
8
+ # the License for the specific language governing permissions and
9
+ # limitations under the License.
10
+
11
+ require 'ec2/amitools/crypto'
12
+ require 'ec2/amitools/format'
13
+ require 'ec2/amitools/xmlutil'
14
+ require 'pathname'
15
+ require 'rexml/document'
16
+
17
+ # Manifest Version 2007-08-29.
18
+ # Not backwards compatible
19
+ class ManifestV20070829
20
+ VERSION_STRING = '2007-08-29'
21
+ VERSION = VERSION_STRING.gsub('-','_').to_i
22
+
23
+ # Expose the version
24
+ def self.version
25
+ VERSION
26
+ end
27
+
28
+ # AMI part information container.
29
+ class PartInformation
30
+ attr_reader :filename # Part's file basename.
31
+ attr_reader :digest # Part's digest hex encoded.
32
+
33
+ # Initialize with part's _filename_ and _digest_ as byte string.
34
+ def initialize(filename, digest)
35
+ @filename , @digest = filename, digest
36
+ end
37
+ end
38
+
39
+ def initialize(xml = nil)
40
+ if xml == nil
41
+ @doc = REXML::Document.new
42
+ else
43
+ # Convert to string if necessary.
44
+ xml = (xml.kind_of?(IO) ? xml.read : xml)
45
+ @doc = REXML::Document.new(xml)
46
+ end
47
+ end
48
+
49
+ # for debugging only
50
+ def doc
51
+ @doc
52
+ end
53
+
54
+ # Initialize the manifest with AMI information.
55
+ # Return +true+ if the initialization was succesful.
56
+ # Raise an exception on error.
57
+ def init(
58
+ name,
59
+ user, # The user's account number.
60
+ arch, # Target architecture for AMI.
61
+ reserved, # Reserved for future use; pass nil.
62
+ parts, # A list of parts filenames and digest pairs.
63
+ size, # The size of the AMI in bytes.
64
+ bundled_size, # The size of the bunled AMI in bytes.
65
+ user_encrypted_key, # Hex encoded.
66
+ ec2_encrypted_key, # Hex encoded.
67
+ cipher_algorithm, # The cipher algorithm used to encrypted the AMI.
68
+ user_encrypted_iv, # Hex encoded.
69
+ ec2_encrypted_iv, # Hex encoded.
70
+ digest, # Hex encoded.
71
+ digest_algorithm, # The digest algorithm.
72
+ privkey_filename, # The user's private key filename.
73
+ bundler_name = nil,
74
+ bundler_version = nil,
75
+ bundler_release = nil)
76
+
77
+ # Check reserved parameters are nil
78
+ raise ArgumentError.new("reserved parameters not nil") unless reserved.nil?
79
+
80
+ # Check non-String parameter types.
81
+ raise ArgumentError.new("parts parameter type invalid") unless parts.is_a? Array
82
+
83
+ # XML document.
84
+ @doc = REXML::Document.new
85
+ @doc << REXML::XMLDecl.new
86
+
87
+ # manifest - the root element.
88
+ manifest = REXML::Element.new('manifest')
89
+
90
+ @doc.add_element(manifest)
91
+
92
+ # version - indicate the manifest version.
93
+ version = REXML::Element.new('version')
94
+ version.text = VERSION
95
+ manifest.add_element(version)
96
+
97
+ # bundler information
98
+ if bundler_name or bundler_version or bundler_release
99
+ bundler_element = REXML::Element.new('bundler')
100
+ manifest.add_element(bundler_element)
101
+
102
+ [['name', bundler_name ],
103
+ ['version', bundler_version ],
104
+ ['release', bundler_release ]].each do |element_name, text|
105
+ if element_name
106
+ element = REXML::Element.new(element_name)
107
+ element.text = text
108
+ bundler_element.add_element(element)
109
+ end
110
+ end
111
+ end
112
+
113
+ # machine_configuration - the target hardware description of the AMI.
114
+ machine_configuration = REXML::Element.new('machine_configuration')
115
+ manifest.add_element(machine_configuration)
116
+
117
+ # image - the image element.
118
+ image = REXML::Element.new('image')
119
+ name_element = REXML::Element.new('name')
120
+ name_element.text = name
121
+ image.add_element(name_element)
122
+ manifest.add_element(image)
123
+
124
+ # user - the user's AWS access key ID.
125
+ user_element = REXML::Element.new('user')
126
+ user_element.text = user
127
+ image.add_element(user_element)
128
+
129
+ # m_c arch - the target hardware architecture
130
+ if arch
131
+ arch_element = REXML::Element.new('architecture')
132
+ arch_element.text = arch
133
+ machine_configuration.add_element(arch_element)
134
+ end
135
+
136
+ # digest - the digest of the AMI.
137
+ digest_element = REXML::Element.new('digest')
138
+ digest_element.add_attribute('algorithm', digest_algorithm)
139
+ digest_element.add_text(digest)
140
+ image.add_element(digest_element)
141
+
142
+ # size - the size of the uncompressed AMI.
143
+ size_element = REXML::Element.new('size')
144
+ size_element.text = size.to_s
145
+ image.add_element(size_element)
146
+
147
+ # bundled size - the size of the bundled AMI.
148
+ bundled_size_element = REXML::Element.new('bundled_size')
149
+ bundled_size_element.text = bundled_size.to_s
150
+ image.add_element(bundled_size_element)
151
+
152
+ # ec2 encrypted key element.
153
+ ec2_encrypted_key_element = REXML::Element.new('ec2_encrypted_key')
154
+ ec2_encrypted_key_element.add_attribute('algorithm', cipher_algorithm)
155
+ ec2_encrypted_key_element.add_text(ec2_encrypted_key)
156
+ image.add_element(ec2_encrypted_key_element)
157
+
158
+ # user encrypted key element.
159
+ user_encrypted_key_element = REXML::Element.new('user_encrypted_key')
160
+ user_encrypted_key_element.add_attribute('algorithm', cipher_algorithm)
161
+ user_encrypted_key_element.add_text(user_encrypted_key)
162
+ image.add_element(user_encrypted_key_element)
163
+
164
+ # ec2 encrypted iv element.
165
+ ec2_encrypted_iv_element = REXML::Element.new('ec2_encrypted_iv')
166
+ ec2_encrypted_iv_element.add_text(ec2_encrypted_iv)
167
+ image.add_element(ec2_encrypted_iv_element)
168
+
169
+ # user encrypted iv element.
170
+ user_encrypted_iv_element = REXML::Element.new('user_encrypted_iv')
171
+ user_encrypted_iv_element.add_text(user_encrypted_iv)
172
+ image.add_element(user_encrypted_iv_element)
173
+
174
+ # parts - list of the image parts.
175
+ parts_element = REXML::Element.new('parts')
176
+ parts_element.add_attributes({'count' => parts.size.to_s})
177
+ index=0
178
+ parts.each do |part|
179
+ # Add image part element for each image part.
180
+ part_element = REXML::Element.new('part')
181
+ part_element.add_attribute('index', index.to_s)
182
+ filename = REXML::Element.new('filename')
183
+ filename.add_text(part[0])
184
+ part_element.add_element(filename)
185
+ digest = REXML::Element.new('digest')
186
+ digest.add_attribute('algorithm', digest_algorithm)
187
+ digest.add_text(Format::bin2hex(part[1]))
188
+ part_element.add_element(digest)
189
+ parts_element.add_element(part_element)
190
+ index+=1
191
+ end
192
+ image.add_element(parts_element)
193
+
194
+ # Sign the manifest.
195
+ sign(privkey_filename)
196
+
197
+ return true
198
+ end
199
+
200
+ def self::version20070829?(xml)
201
+ doc = REXML::Document.new(xml)
202
+ version = REXML::XPath.first(doc.root, 'version')
203
+ return true if (version and version.text and version.text == VERSION_STRING)
204
+ return (version and version.text and version.text == VERSION.to_s)
205
+ end
206
+
207
+ # Return the AMI's digest hex encoded.
208
+ def digest()
209
+ return get_element_text('image/digest')
210
+ end
211
+
212
+ def name()
213
+ return get_element_text('image/name')
214
+ end
215
+
216
+ # The ec2 encrypted key hex encoded.
217
+ def ec2_encrypted_key()
218
+ return get_element_text('image/ec2_encrypted_key')
219
+ end
220
+
221
+ # The user encrypted key hex encoded.
222
+ def user_encrypted_key()
223
+ return get_element_text('image/user_encrypted_key')
224
+ end
225
+
226
+ # The ec2 encrypted initialization vector hex encoded.
227
+ def ec2_encrypted_iv()
228
+ return get_element_text('image/ec2_encrypted_iv')
229
+ end
230
+
231
+ # The user encrypted initialization vector hex encoded.
232
+ def user_encrypted_iv()
233
+ return get_element_text('image/user_encrypted_iv')
234
+ end
235
+
236
+ # Get digest algorithm used.
237
+ def digest_algorithm()
238
+ return REXML::XPath.first(@doc.root, 'image/digest/@algorithm').to_s
239
+ end
240
+
241
+ # Get cipher algorithm used.
242
+ def cipher_algorithm()
243
+ return REXML::XPath.first(@doc.root, 'image/ec2_encrypted_key/@algorithm').to_s
244
+ end
245
+
246
+ # Retrieve a list of AMI bundle parts info. Each element is a hash
247
+ # with the following elements:
248
+ # * 'digest'
249
+ # * 'filename'
250
+ # * 'index'
251
+ def ami_part_info_list
252
+ parts = Array.new
253
+ REXML::XPath.each(@doc.root,'image/parts/part') do |part|
254
+ index = part.attribute('index').to_s.to_i
255
+ filename = REXML::XPath.first(part, 'filename').text
256
+ digest = REXML::XPath.first(part, 'digest').text
257
+ parts << { 'digest'=>digest, 'filename'=>filename, 'index'=>index }
258
+ end
259
+ return parts
260
+ end
261
+
262
+ # A list of PartInformation instances representing the AMI parts.
263
+ def parts()
264
+ parts = []
265
+ REXML::XPath.each(@doc.root,'image/parts/part') do |part|
266
+ index = part.attribute('index').to_s.to_i
267
+ filename = REXML::XPath.first(part, 'filename').text
268
+ digest = Format::hex2bin(REXML::XPath.first(part, 'digest').text)
269
+ parts[index] = PartInformation.new(filename, digest)
270
+ end
271
+ return parts
272
+ end
273
+
274
+ # Return the size of the AMI.
275
+ def size()
276
+ return get_element_text('image/size').to_i()
277
+ end
278
+
279
+ # Return the (optional) architecture of the AMI.
280
+ def arch()
281
+ return get_element_text('machine_configuration/architecture')
282
+ end
283
+
284
+ # Return the bundled size of the AMI.
285
+ def bundled_size()
286
+ return get_element_text('image/bundled_size').to_i
287
+ end
288
+
289
+ # Return the bundler name.
290
+ def bundler_name()
291
+ return get_element_text('bundler/name')
292
+ end
293
+
294
+ # Return the bundler version.
295
+ def bundler_version()
296
+ return get_element_text('bundler/version')
297
+ end
298
+
299
+ # Return the bundler release.
300
+ def bundler_release()
301
+ return get_element_text('bundler/release')
302
+ end
303
+
304
+ # Sign the manifest. If it is already signed, the signature and certificate
305
+ # will be replaced
306
+ def sign(privkey_filename)
307
+ unless privkey_filename.kind_of? String and File::exist?(privkey_filename)
308
+ raise ArgumentError.new("privkey_filename parameter invalid")
309
+ end
310
+
311
+ # Get the XML for <machine_configuration> and <image> elements and sign them.
312
+ machine_configuration_xml = XMLUtil.get_xml(@doc.to_s, 'machine_configuration') || ""
313
+ image_xml = XMLUtil.get_xml(@doc.to_s, 'image')
314
+ sig = Crypto::sign(machine_configuration_xml + image_xml, privkey_filename)
315
+
316
+ # Create the signature and certificate elements.
317
+ signature = REXML::Element.new('signature')
318
+ signature.add_text(Format::bin2hex(sig))
319
+ @doc.root.delete_element('signature')
320
+ @doc.root.add_element(signature)
321
+ end
322
+
323
+ # Return the signature
324
+ def signature
325
+ get_element_text('signature')
326
+ end
327
+
328
+ # Verify the signature
329
+ def authenticate(cert)
330
+ machine_configuration_xml = XMLUtil.get_xml(@doc.to_s, 'machine_configuration') || ""
331
+ image_xml = XMLUtil.get_xml(@doc.to_s, 'image')
332
+ pubkey = Crypto::cert2pubkey(cert)
333
+ Crypto::authenticate(machine_configuration_xml + image_xml, Format::hex2bin(signature), pubkey)
334
+ end
335
+
336
+ # Return the manifest as an XML string.
337
+ def to_s()
338
+ return @doc.to_s
339
+ end
340
+
341
+ def user()
342
+ return get_element_text('image/user')
343
+ end
344
+
345
+ def version()
346
+ return get_element_text('version').to_i
347
+ end
348
+
349
+ private
350
+
351
+ def get_element_text(xpath)
352
+ element = REXML::XPath.first(@doc.root, xpath)
353
+ unless element
354
+ raise "invalid AMI manifest, #{xpath} element not present"
355
+ end
356
+ unless element.text
357
+ raise "invalid AMI manifest, #{xpath} element empty"
358
+ end
359
+ return element.text
360
+ end
361
+ end