ec2_amitools 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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