ec2_amitools 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,95 @@
|
|
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
|
+
module EC2
|
12
|
+
module Common
|
13
|
+
class Headers
|
14
|
+
MANDATORY = ['content-md5', 'content-type', 'date'] # Order matters.
|
15
|
+
X_AMZ_PREFIX = 'x-amz'
|
16
|
+
X_AMZ_SECURITY_TOKEN = 'x-amz-security-token'
|
17
|
+
|
18
|
+
def initialize(verb)
|
19
|
+
raise ArgumentError.new('invalid verb') if verb.to_s.empty?
|
20
|
+
@headers = {}
|
21
|
+
@verb = verb
|
22
|
+
end
|
23
|
+
|
24
|
+
#---------------------------------------------------------------------
|
25
|
+
# Add a Header key-value pair.
|
26
|
+
def add(name, value)
|
27
|
+
raise ArgumentError.new("name '#{name.inspect}' must be a String") unless name.is_a? String
|
28
|
+
raise ArgumentError.new("value '#{value.inspect}' (#{name}) must be a String") unless value.is_a? String
|
29
|
+
@headers[name.downcase.strip] = value.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
#-----------------------------------------------------------------------
|
33
|
+
# Sign the headers using HMAC SHA1.
|
34
|
+
def sign(creds, aws_secret_access_key, url, bucket)
|
35
|
+
aws_access_key_id = creds
|
36
|
+
delegation_token = nil
|
37
|
+
if (creds.is_a?(Hash))
|
38
|
+
aws_access_key_id = creds['aws_access_key_id']
|
39
|
+
aws_secret_access_key = creds['aws_secret_access_key']
|
40
|
+
delegation_token = creds['aws_delegation_token']
|
41
|
+
end
|
42
|
+
|
43
|
+
@headers['date'] = Time.now.httpdate # add HTTP Date header
|
44
|
+
@headers['content-type'] ||= 'application/x-www-form-urlencoded'
|
45
|
+
|
46
|
+
# Build the data to sign.
|
47
|
+
data = @verb + "\n"
|
48
|
+
|
49
|
+
# Deal with mandatory headers first:
|
50
|
+
MANDATORY.each do |name|
|
51
|
+
data += @headers.fetch(name, "") + "\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
unless delegation_token.nil?
|
55
|
+
@headers[X_AMZ_SECURITY_TOKEN] = delegation_token
|
56
|
+
end
|
57
|
+
# Add mandatory headers and those that start with the x-amz prefix.
|
58
|
+
@headers.sort.each do |name, value|
|
59
|
+
# Headers that start with x-amz must have both their name and value
|
60
|
+
# added.
|
61
|
+
if name =~ /^#{X_AMZ_PREFIX}/
|
62
|
+
data += name + ":" + value +"\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
uri = URI.parse(url)
|
67
|
+
# Ignore everything in the URL after the question mark unless, by the
|
68
|
+
# S3 protocol, it signifies an acl, torrent, logging or location parameter
|
69
|
+
if uri.host.start_with? bucket
|
70
|
+
data << "/#{bucket}"
|
71
|
+
end
|
72
|
+
data << if uri.path.empty?
|
73
|
+
"/"
|
74
|
+
else
|
75
|
+
uri.path
|
76
|
+
end
|
77
|
+
['acl', 'logging', 'torrent', 'location'].each do |item|
|
78
|
+
regex = Regexp.new("[&?]#{item}($|&|=)")
|
79
|
+
data << '?' + item if regex.match(url)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sign headers and then put signature back into headers.
|
83
|
+
signature = Base64.encode64(Crypto::hmac_sha1(aws_secret_access_key, data))
|
84
|
+
signature.chomp!
|
85
|
+
@headers['Authorization'] = "AWS #{aws_access_key_id}:#{signature}"
|
86
|
+
end
|
87
|
+
|
88
|
+
#-----------------------------------------------------------------------
|
89
|
+
# Return the headers as a map from header name to header value.
|
90
|
+
def get
|
91
|
+
return @headers.clone
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,173 @@
|
|
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
|
+
#
|
12
|
+
# A class to contain headers and sign them with signature version 4
|
13
|
+
# This code is a shameless copy of our SDK's signature version 4 code base
|
14
|
+
#
|
15
|
+
|
16
|
+
require 'cgi'
|
17
|
+
require 'uri'
|
18
|
+
require 'time'
|
19
|
+
require 'base64'
|
20
|
+
require 'tempfile'
|
21
|
+
require 'digest/md5'
|
22
|
+
require 'openssl'
|
23
|
+
require 'digest'
|
24
|
+
require 'digest/sha2'
|
25
|
+
|
26
|
+
module EC2
|
27
|
+
module Common
|
28
|
+
class HeadersV4
|
29
|
+
# Create an HttpHeaderV4,
|
30
|
+
# values = {
|
31
|
+
# :host -> http host
|
32
|
+
# :hexdigest_body -> hexdigest of the http request's body
|
33
|
+
# :region -> region of the endpoint
|
34
|
+
# :service -> the service that should recieve this request
|
35
|
+
# :http_method -> http method
|
36
|
+
# :path -> URI
|
37
|
+
# :querystring -> Everything after ? in URI
|
38
|
+
# :access_key_id -> access key
|
39
|
+
# :secret_access_key -> secret access kry
|
40
|
+
# [optional] :fixed_datetime -> Fix the datetime using DateTime object, do not use Time.now
|
41
|
+
# }
|
42
|
+
# For more info see:
|
43
|
+
# http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
44
|
+
def initialize values, headers={}
|
45
|
+
@headers = headers
|
46
|
+
@host = values[:host]
|
47
|
+
@hexdigest_body = values[:hexdigest_body]
|
48
|
+
@region = values[:region]
|
49
|
+
@service = values[:service]
|
50
|
+
@http_method = values[:http_method]
|
51
|
+
@path = values[:path]
|
52
|
+
@querystring = values[:querystring]
|
53
|
+
@access_key_id = values[:access_key_id]
|
54
|
+
@secret_access_key = values[:secret_access_key]
|
55
|
+
@fixed_datetime = values[:fixed_datetime]
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_authorization!
|
59
|
+
datetime = get_datetime
|
60
|
+
@headers['host'] = @host
|
61
|
+
@headers['x-amz-date'] = datetime
|
62
|
+
@headers['x-amz-content-sha256'] ||= @hexdigest_body || EC2::Common::HeadersV4::hexdigest('')
|
63
|
+
@headers['authorization'] = authorization(datetime)
|
64
|
+
@headers
|
65
|
+
end
|
66
|
+
|
67
|
+
def authorization datetime
|
68
|
+
parts = []
|
69
|
+
parts << "AWS4-HMAC-SHA256 Credential=#{credential(datetime)}"
|
70
|
+
parts << "SignedHeaders=#{signed_headers}"
|
71
|
+
parts << "Signature=#{signature(datetime)}"
|
72
|
+
parts.join(', ')
|
73
|
+
end
|
74
|
+
|
75
|
+
def signature datetime
|
76
|
+
k_secret = @secret_access_key
|
77
|
+
k_date = hmac("AWS4" + k_secret, datetime[0,8])
|
78
|
+
k_region = hmac(k_date, @region)
|
79
|
+
k_service = hmac(k_region, @service)
|
80
|
+
k_credentials = hmac(k_service, 'aws4_request')
|
81
|
+
hexhmac(k_credentials, string_to_sign(datetime))
|
82
|
+
end
|
83
|
+
|
84
|
+
def string_to_sign datetime
|
85
|
+
parts = []
|
86
|
+
parts << 'AWS4-HMAC-SHA256'
|
87
|
+
parts << datetime
|
88
|
+
parts << credential_string(datetime)
|
89
|
+
parts << EC2::Common::HeadersV4::hexdigest(canonical_request)
|
90
|
+
parts.join("\n")
|
91
|
+
end
|
92
|
+
|
93
|
+
def credential datetime
|
94
|
+
"#{@access_key_id}/#{credential_string(datetime)}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def credential_string datetime
|
98
|
+
parts = []
|
99
|
+
parts << datetime[0,8]
|
100
|
+
parts << @region
|
101
|
+
parts << @service
|
102
|
+
parts << 'aws4_request'
|
103
|
+
parts.join("/")
|
104
|
+
end
|
105
|
+
|
106
|
+
def canonical_request
|
107
|
+
parts = []
|
108
|
+
parts << @http_method
|
109
|
+
parts << CGI::unescape(@path)
|
110
|
+
parts << canonical_querystring
|
111
|
+
parts << canonical_headers + "\n"
|
112
|
+
parts << signed_headers
|
113
|
+
parts << @headers['x-amz-content-sha256']
|
114
|
+
parts.join("\n")
|
115
|
+
end
|
116
|
+
|
117
|
+
def signed_headers
|
118
|
+
to_sign = @headers.keys.map{|k| k.to_s.downcase}
|
119
|
+
to_sign.delete('authorization')
|
120
|
+
to_sign.sort.join(";")
|
121
|
+
end
|
122
|
+
|
123
|
+
def canonical_querystring
|
124
|
+
CGI::parse(@querystring).sort_by{|k,v| CGI::escape(k)}.map do |v|
|
125
|
+
value = v[1][0] || ""
|
126
|
+
"#{CGI::escape(v[0])}=#{CGI::escape(value)}"
|
127
|
+
end.join('&')
|
128
|
+
end
|
129
|
+
|
130
|
+
def canonical_headers
|
131
|
+
headers = []
|
132
|
+
@headers.each_pair do |k,v|
|
133
|
+
k_lower = k.downcase
|
134
|
+
headers << [k_lower,v] unless k_lower == 'authorization'
|
135
|
+
end
|
136
|
+
headers = headers.sort_by{|k,v| k}
|
137
|
+
headers.map{|k,v| "#{k}:#{canonical_header_values(v)}"}.join("\n")
|
138
|
+
end
|
139
|
+
|
140
|
+
def canonical_header_values values
|
141
|
+
values = [values] unless values.is_a?(Array)
|
142
|
+
values.map{|v|v.to_s}.join(',').gsub(/\s+/, ' ').strip
|
143
|
+
end
|
144
|
+
|
145
|
+
def hmac key, value
|
146
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, value)
|
147
|
+
end
|
148
|
+
|
149
|
+
def hexhmac key, value
|
150
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha256'), key, value)
|
151
|
+
end
|
152
|
+
|
153
|
+
def get_datetime
|
154
|
+
return @fixed_datetime.strftime("%Y%m%dT%H%M%SZ") if @fixed_datetime != nil
|
155
|
+
Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns a SHA256 hexdigest of value.
|
159
|
+
# Should be used to hexdigest body of http request.
|
160
|
+
def self.hexdigest value, chunk_size = 1024 * 1024
|
161
|
+
digest = Digest::SHA256.new
|
162
|
+
if value.respond_to?(:read)
|
163
|
+
chunk = nil
|
164
|
+
digest.update(chunk) while chunk = value.read(chunk_size)
|
165
|
+
value.rewind
|
166
|
+
else
|
167
|
+
digest.update(value)
|
168
|
+
end
|
169
|
+
digest.hexdigest
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,333 @@
|
|
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
|
+
# ---------------------------------------------------------------------------
|
12
|
+
# Module that provides http functionality.
|
13
|
+
# ---------------------------------------------------------------------------
|
14
|
+
require 'uri'
|
15
|
+
require 'set'
|
16
|
+
require 'time'
|
17
|
+
require 'base64'
|
18
|
+
require 'tmpdir'
|
19
|
+
require 'tempfile'
|
20
|
+
require 'fileutils'
|
21
|
+
|
22
|
+
require 'ec2/common/constants'
|
23
|
+
require 'ec2/common/curl'
|
24
|
+
require 'ec2/common/signature'
|
25
|
+
require 'ec2/common/headers'
|
26
|
+
require 'ec2/amitools/crypto'
|
27
|
+
|
28
|
+
module EC2
|
29
|
+
module Common
|
30
|
+
module HTTP
|
31
|
+
DEFAULT_SIGV = EC2::Common::SIGV4
|
32
|
+
DEFAULT_REGION = 'us-east-1'
|
33
|
+
|
34
|
+
class Response < EC2::Common::Curl::Response
|
35
|
+
attr_reader :body
|
36
|
+
def initialize(code, type, body=nil)
|
37
|
+
super code, type
|
38
|
+
@body = body
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#-----------------------------------------------------------------------
|
43
|
+
# Errors.
|
44
|
+
class Error < RuntimeError
|
45
|
+
attr_reader :code
|
46
|
+
def initialize(msg, code = nil)
|
47
|
+
super(msg)
|
48
|
+
@code = code || 1
|
49
|
+
end
|
50
|
+
class PathInvalid < Error; end
|
51
|
+
class Write < Error; end
|
52
|
+
class BadDigest < Error
|
53
|
+
def initialize(file, expected, obtained)
|
54
|
+
super("Digest for file '#{file}' #{obtained} differs from expected digest #{digest}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class Transfer < Error; end
|
58
|
+
class Retrieve < Transfer; end
|
59
|
+
class Redirect < Error
|
60
|
+
attr_accessor :code, :endpoint
|
61
|
+
def initialize(code, endpoint)
|
62
|
+
super("Redirected (#{code}) to new endpoint: #{endpoint}")
|
63
|
+
@code = code
|
64
|
+
@endpoint = endpoint
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
#-----------------------------------------------------------------------
|
71
|
+
# Invoke curl with arguments given and process results of output file
|
72
|
+
def HTTP::invoke(url, arguments, outfile, debug=false)
|
73
|
+
begin
|
74
|
+
raise ArgumentError.new(outfile) unless File.exists? outfile
|
75
|
+
result = EC2::Common::Curl.execute("#{arguments.join(' ')} '#{url}'", debug)
|
76
|
+
if result.success?
|
77
|
+
if result.response.success?
|
78
|
+
return result.response
|
79
|
+
else
|
80
|
+
synopsis= 'Server.Error(' + result.response.code.to_s + '): '
|
81
|
+
message = result.stderr + ' '
|
82
|
+
if result.response.type == 'application/xml'
|
83
|
+
require 'rexml/document'
|
84
|
+
doc = REXML::Document.new(IO.read(outfile))
|
85
|
+
if doc.root
|
86
|
+
if result.response.redirect?
|
87
|
+
endpoint = REXML::XPath.first(doc, '/Error/Endpoint').text
|
88
|
+
raise Error::Redirect.new(result.response.code, endpoint)
|
89
|
+
end
|
90
|
+
content = REXML::XPath.first(doc, '/Error/Code')
|
91
|
+
unless content.nil? or content.text.empty?
|
92
|
+
synopsis= 'Server.'+ content.text + '(' +
|
93
|
+
result.response.code.to_s + '): '
|
94
|
+
end
|
95
|
+
content = REXML::XPath.first(doc, '/Error/Message')
|
96
|
+
message = content.text unless content.nil?
|
97
|
+
end
|
98
|
+
else
|
99
|
+
if result.response.type =~ /text/
|
100
|
+
message << IO.read(outfile)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
raise Error::Transfer.new(synopsis + message, result.response.code)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
synopsis= 'Curl.Error(' + result.status.to_s + '): '
|
107
|
+
message = result.stderr.split("\n").map { |line|
|
108
|
+
if (m = /^curl:\s+(?:\(\d{1,2}\)\s+)*(.*)$/.match(line))
|
109
|
+
(m.captures[0] == "try 'curl --help' for more information") ?
|
110
|
+
'' : m.captures[0]
|
111
|
+
else
|
112
|
+
line.strip
|
113
|
+
end
|
114
|
+
}.join("\n")
|
115
|
+
output = result.stdout.chomp
|
116
|
+
if debug and not output.empty?
|
117
|
+
message << "\nCurl.Output: " + output.gsub("\n", "\nCurl.Output: ")
|
118
|
+
end
|
119
|
+
raise Error::Transfer.new(synopsis + message.strip + '.', result.status)
|
120
|
+
end
|
121
|
+
rescue EC2::Common::Curl::Error => e
|
122
|
+
raise Error::Transfer.new(e.message, e.code)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#-----------------------------------------------------------------------
|
127
|
+
# Delete the file at the specified url.
|
128
|
+
def HTTP::delete(url, bucket, options={}, user=nil, pass=nil, debug=false, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
|
129
|
+
raise ArgumentError.new('Bad options in HTTP::delete') unless options.is_a? Hash
|
130
|
+
begin
|
131
|
+
output = Tempfile.new('ec2-delete-response')
|
132
|
+
output.close
|
133
|
+
|
134
|
+
arguments = ['-X DELETE']
|
135
|
+
arguments << get_arguments(url, bucket, 'DELETE', options, user, pass, sigv, region)
|
136
|
+
arguments << '-o ' + output.path
|
137
|
+
|
138
|
+
response = HTTP::invoke(url, arguments, output.path, debug)
|
139
|
+
return EC2::Common::HTTP::Response.new(response.code, response.type)
|
140
|
+
ensure
|
141
|
+
output.close(true)
|
142
|
+
GC.start
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
#-----------------------------------------------------------------------
|
148
|
+
# Put the file at the specified path to the specified url. The content of
|
149
|
+
# the options hash will be passed as HTTP headers. If the username and
|
150
|
+
# password options are specified, then the headers will be signed.
|
151
|
+
def HTTP::put(url, bucket, path, options={}, user=nil, pass=nil, debug=false, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
|
152
|
+
path ||= "/dev/null"
|
153
|
+
raise Error::PathInvalid.new(path) unless path and File::exist?(path)
|
154
|
+
raise ArgumentError.new('Bad options in HTTP::put') unless options.is_a? Hash
|
155
|
+
|
156
|
+
begin
|
157
|
+
output = Tempfile.new('ec2-put-response')
|
158
|
+
output.close
|
159
|
+
|
160
|
+
arguments = [get_arguments(url, bucket, 'PUT', options, user, pass, sigv, region, open(path))]
|
161
|
+
arguments << '-T ' + path
|
162
|
+
arguments << '-o ' + output.path
|
163
|
+
|
164
|
+
response = HTTP::invoke(url, arguments, output.path, debug)
|
165
|
+
return EC2::Common::HTTP::Response.new(response.code, response.type)
|
166
|
+
ensure
|
167
|
+
output.close(true)
|
168
|
+
GC.start
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#-----------------------------------------------------------------------
|
173
|
+
# Create a bucket using the specified url and constraints (either a file
|
174
|
+
# with location string, or the string itself). The content of the
|
175
|
+
# options hash will be passed as HTTP headers. If the username and
|
176
|
+
# password options are specified, then the headers will be signed.
|
177
|
+
def HTTP::putdir(url, bucket, constraint, options={}, user=nil, pass=nil, debug=false, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
|
178
|
+
if constraint and File::exists?(constraint)
|
179
|
+
putdir_file(url, bucket, constraint, options, user, pass, debug)
|
180
|
+
else
|
181
|
+
putdir_binary_data(url, bucket, constraint, options, user, pass, debug, sigv, region)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def HTTP::putdir_file(url, bucket, path, options={}, user=nil, pass=nil, debug=false)
|
186
|
+
raise Error::PathInvalid.new(path) unless path and File::exist?(path)
|
187
|
+
raise ArgumentError.new('Bad options in HTTP::putdir_file') unless options.is_a? Hash
|
188
|
+
|
189
|
+
begin
|
190
|
+
output = Tempfile.new('ec2-put-response')
|
191
|
+
output.close
|
192
|
+
arguments = []
|
193
|
+
|
194
|
+
headers = EC2::Common::Headers.new('PUT')
|
195
|
+
options.each do |name, value| headers.add(name, value) end
|
196
|
+
headers.sign(user, pass, url, bucket) if user and pass
|
197
|
+
|
198
|
+
arguments << headers.get.map { |name, value| "-H \"#{name}:#{value}\""}.join(' ')
|
199
|
+
arguments << '-T - '
|
200
|
+
arguments << '-o ' + output.path
|
201
|
+
arguments << " < #{path}"
|
202
|
+
|
203
|
+
response = HTTP::invoke(url, arguments, output.path, debug)
|
204
|
+
return EC2::Common::HTTP::Response.new(response.code, response.type)
|
205
|
+
ensure
|
206
|
+
output.close(true)
|
207
|
+
GC.start
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def HTTP::putdir_binary_data(url, bucket, binary_data, options={}, user=nil, pass=nil, debug=false, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
|
212
|
+
raise ArgumentError.new('Bad options in HTTP::putdir_binary_data') unless options.is_a? Hash
|
213
|
+
|
214
|
+
begin
|
215
|
+
output = Tempfile.new('ec2-put-response')
|
216
|
+
output.close
|
217
|
+
|
218
|
+
arguments = ["-X PUT"]
|
219
|
+
arguments << "--data-binary \"#{binary_data}\""
|
220
|
+
arguments << get_arguments(url, bucket, 'PUT', options, user, pass, sigv, region, binary_data)
|
221
|
+
arguments << '-o ' + output.path
|
222
|
+
|
223
|
+
response = HTTP::invoke(url, arguments, output.path, debug)
|
224
|
+
return EC2::Common::HTTP::Response.new(response.code, response.type)
|
225
|
+
ensure
|
226
|
+
output.close(true)
|
227
|
+
GC.start
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
#-----------------------------------------------------------------------
|
232
|
+
# Save the file at specified url, to the local file at specified path.
|
233
|
+
# The local file will be created if necessary, or overwritten already
|
234
|
+
# existing. If specified, the expected digest is compare to that of the
|
235
|
+
# retrieved file which gets deleted if the calculated digest does not meet
|
236
|
+
# expectations. If no path is specified, and the response is a 200 OK, the
|
237
|
+
# content of the response will be returned as a String
|
238
|
+
def HTTP::get(url, bucket, path=nil, options={}, user=nil, pass=nil,
|
239
|
+
size=nil, digest=nil, debug=false, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
|
240
|
+
raise ArgumentError.new('Bad options in HTTP::get') unless options.is_a? Hash
|
241
|
+
buffer = nil
|
242
|
+
if path.nil?
|
243
|
+
buffer = Tempfile.new('ec2-get-response')
|
244
|
+
buffer.close
|
245
|
+
path = buffer.path
|
246
|
+
else
|
247
|
+
directory = File.dirname(path)
|
248
|
+
FileUtils.mkdir_p(directory) unless File.exist?(directory)
|
249
|
+
end
|
250
|
+
|
251
|
+
arguments = [get_arguments(url, bucket, 'GET', options, user, pass, sigv, region)]
|
252
|
+
arguments << "--max-filesize #{size}" if size
|
253
|
+
arguments << '-o ' + path
|
254
|
+
|
255
|
+
begin
|
256
|
+
FileUtils.touch path
|
257
|
+
response = HTTP::invoke(url, arguments, path, debug)
|
258
|
+
body = nil
|
259
|
+
if response.success?
|
260
|
+
if digest
|
261
|
+
obtained = IO.popen("openssl sha1 #{path}") { |io| io.readline.split(/\s+/).last.strip }
|
262
|
+
unless digest == obtained
|
263
|
+
File.delete(path) if File.exists?(path) and not buffer.is_a? Tempfile
|
264
|
+
raise Error::BadDigest.new(path, digest, obtained)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
if buffer.is_a? Tempfile
|
268
|
+
buffer.open;
|
269
|
+
body = buffer.read
|
270
|
+
end
|
271
|
+
else
|
272
|
+
File.delete(path) if File.exist?(path) and not buffer.is_a? Tempfile
|
273
|
+
end
|
274
|
+
return EC2::Common::HTTP::Response.new(response.code, response.type, body)
|
275
|
+
rescue Error::Transfer => e
|
276
|
+
File::delete(path) if File::exist?(path) and not buffer.is_a? Tempfile
|
277
|
+
raise Error::Retrieve.new(e.message, e.code)
|
278
|
+
ensure
|
279
|
+
if buffer.is_a? Tempfile
|
280
|
+
buffer.close
|
281
|
+
buffer.unlink if File.exists? path
|
282
|
+
GC.start
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
#-----------------------------------------------------------------------
|
289
|
+
# Get the HEAD response for the specified url.
|
290
|
+
def HTTP::head(url, bucket, options={}, user=nil, pass=nil, debug=false, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
|
291
|
+
raise ArgumentError.new('Bad options in HTTP::head') unless options.is_a? Hash
|
292
|
+
begin
|
293
|
+
output = Tempfile.new('ec2-head-response')
|
294
|
+
output.close
|
295
|
+
|
296
|
+
arguments = ['--head']
|
297
|
+
arguments << get_arguments(url, bucket, 'HEAD', options, user, pass, sigv, region)
|
298
|
+
arguments << '-o ' + output.path
|
299
|
+
|
300
|
+
response = HTTP::invoke(url, arguments, output.path, debug)
|
301
|
+
return EC2::Common::HTTP::Response.new(response.code, response.type)
|
302
|
+
|
303
|
+
rescue Error::Transfer => e
|
304
|
+
raise Error::Retrieve.new(e.message, e.code)
|
305
|
+
ensure
|
306
|
+
output.close(true)
|
307
|
+
GC.start
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
private
|
312
|
+
def HTTP::get_arguments(url, bucket, http_method, options, user, pass, sigv, region, file_path=nil)
|
313
|
+
headers = if user and pass
|
314
|
+
if sigv == EC2::Common::SIGV2
|
315
|
+
EC2::Common::Signature::curl_args_sigv2(url, bucket,
|
316
|
+
http_method,
|
317
|
+
options,
|
318
|
+
user, pass)
|
319
|
+
elsif sigv == EC2::Common::SIGV4
|
320
|
+
EC2::Common::Signature::curl_args_sigv4(url, region, bucket,
|
321
|
+
http_method,
|
322
|
+
options,
|
323
|
+
user, pass,
|
324
|
+
file_path)
|
325
|
+
end
|
326
|
+
else
|
327
|
+
options
|
328
|
+
end
|
329
|
+
headers.map { |name, value| "-H \"#{name}:#{value}\""}.join(' ')
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|