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.
- checksums.yaml +7 -0
- data/README.md +54 -0
- data/bin/console +14 -0
- data/bin/ec2-ami-tools-version +6 -0
- data/bin/ec2-bundle-image +6 -0
- data/bin/ec2-bundle-vol +6 -0
- data/bin/ec2-delete-bundle +6 -0
- data/bin/ec2-download-bundle +6 -0
- data/bin/ec2-migrate-bundle +6 -0
- data/bin/ec2-migrate-manifest +6 -0
- data/bin/ec2-unbundle +6 -0
- data/bin/ec2-upload-bundle +6 -0
- data/bin/setup +8 -0
- data/etc/ec2/amitools/cert-ec2-cn-north-1.pem +28 -0
- data/etc/ec2/amitools/cert-ec2-gov.pem +17 -0
- data/etc/ec2/amitools/cert-ec2.pem +23 -0
- data/etc/ec2/amitools/mappings.csv +9 -0
- data/lib/ec2/amitools/bundle.rb +251 -0
- data/lib/ec2/amitools/bundle_base.rb +58 -0
- data/lib/ec2/amitools/bundleimage.rb +94 -0
- data/lib/ec2/amitools/bundleimageparameters.rb +42 -0
- data/lib/ec2/amitools/bundlemachineparameters.rb +60 -0
- data/lib/ec2/amitools/bundleparameters.rb +120 -0
- data/lib/ec2/amitools/bundlevol.rb +240 -0
- data/lib/ec2/amitools/bundlevolparameters.rb +164 -0
- data/lib/ec2/amitools/crypto.rb +379 -0
- data/lib/ec2/amitools/decryptmanifest.rb +20 -0
- data/lib/ec2/amitools/defaults.rb +12 -0
- data/lib/ec2/amitools/deletebundle.rb +212 -0
- data/lib/ec2/amitools/deletebundleparameters.rb +78 -0
- data/lib/ec2/amitools/downloadbundle.rb +161 -0
- data/lib/ec2/amitools/downloadbundleparameters.rb +84 -0
- data/lib/ec2/amitools/exception.rb +86 -0
- data/lib/ec2/amitools/fileutil.rb +219 -0
- data/lib/ec2/amitools/format.rb +127 -0
- data/lib/ec2/amitools/instance-data.rb +97 -0
- data/lib/ec2/amitools/manifest_wrapper.rb +132 -0
- data/lib/ec2/amitools/manifestv20070829.rb +361 -0
- data/lib/ec2/amitools/manifestv20071010.rb +403 -0
- data/lib/ec2/amitools/manifestv3.rb +331 -0
- data/lib/ec2/amitools/mapids.rb +148 -0
- data/lib/ec2/amitools/migratebundle.rb +222 -0
- data/lib/ec2/amitools/migratebundleparameters.rb +173 -0
- data/lib/ec2/amitools/migratemanifest.rb +225 -0
- data/lib/ec2/amitools/migratemanifestparameters.rb +118 -0
- data/lib/ec2/amitools/minimalec2.rb +116 -0
- data/lib/ec2/amitools/parameter_exceptions.rb +34 -0
- data/lib/ec2/amitools/parameters_base.rb +168 -0
- data/lib/ec2/amitools/region.rb +93 -0
- data/lib/ec2/amitools/s3toolparameters.rb +183 -0
- data/lib/ec2/amitools/showversion.rb +12 -0
- data/lib/ec2/amitools/syschecks.rb +27 -0
- data/lib/ec2/amitools/tool_base.rb +224 -0
- data/lib/ec2/amitools/unbundle.rb +107 -0
- data/lib/ec2/amitools/unbundleparameters.rb +65 -0
- data/lib/ec2/amitools/uploadbundle.rb +361 -0
- data/lib/ec2/amitools/uploadbundleparameters.rb +108 -0
- data/lib/ec2/amitools/util.rb +532 -0
- data/lib/ec2/amitools/version.rb +33 -0
- data/lib/ec2/amitools/xmlbuilder.rb +237 -0
- data/lib/ec2/amitools/xmlutil.rb +55 -0
- data/lib/ec2/common/constants.rb +16 -0
- data/lib/ec2/common/curl.rb +110 -0
- data/lib/ec2/common/headers.rb +95 -0
- data/lib/ec2/common/headersv4.rb +173 -0
- data/lib/ec2/common/http.rb +333 -0
- data/lib/ec2/common/s3support.rb +231 -0
- data/lib/ec2/common/signature.rb +68 -0
- data/lib/ec2/oem/LICENSE.txt +58 -0
- data/lib/ec2/oem/open4.rb +399 -0
- data/lib/ec2/platform/base/architecture.rb +26 -0
- data/lib/ec2/platform/base/constants.rb +54 -0
- data/lib/ec2/platform/base/pipeline.rb +181 -0
- data/lib/ec2/platform/base.rb +57 -0
- data/lib/ec2/platform/current.rb +55 -0
- data/lib/ec2/platform/linux/architecture.rb +35 -0
- data/lib/ec2/platform/linux/constants.rb +23 -0
- data/lib/ec2/platform/linux/fstab.rb +99 -0
- data/lib/ec2/platform/linux/identity.rb +16 -0
- data/lib/ec2/platform/linux/image.rb +811 -0
- data/lib/ec2/platform/linux/mtab.rb +74 -0
- data/lib/ec2/platform/linux/pipeline.rb +40 -0
- data/lib/ec2/platform/linux/rsync.rb +114 -0
- data/lib/ec2/platform/linux/tar.rb +124 -0
- data/lib/ec2/platform/linux/uname.rb +50 -0
- data/lib/ec2/platform/linux.rb +83 -0
- data/lib/ec2/platform/solaris/architecture.rb +28 -0
- data/lib/ec2/platform/solaris/constants.rb +30 -0
- data/lib/ec2/platform/solaris/fstab.rb +43 -0
- data/lib/ec2/platform/solaris/identity.rb +16 -0
- data/lib/ec2/platform/solaris/image.rb +327 -0
- data/lib/ec2/platform/solaris/mtab.rb +29 -0
- data/lib/ec2/platform/solaris/pipeline.rb +40 -0
- data/lib/ec2/platform/solaris/rsync.rb +24 -0
- data/lib/ec2/platform/solaris/tar.rb +36 -0
- data/lib/ec2/platform/solaris/uname.rb +21 -0
- data/lib/ec2/platform/solaris.rb +38 -0
- data/lib/ec2/platform.rb +69 -0
- data/lib/ec2/version.rb +8 -0
- data/lib/ec2_amitools +1 -0
- data/lib/ec2_amitools.rb +7 -0
- 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
|