5
5
# than 2.0. Testing on multiple Ruby versions is required for
6
6
# changes to this part of the code.
7
7
##################################################################
8
- require 'json'
9
-
10
8
class Proxy
11
9
instance_methods . each do |m |
12
10
undef_method m unless m =~ /(^__|^send$|^object_id$)/
42
40
43
41
@log . level = Logger ::INFO
44
42
43
+ require 'net/http'
44
+
45
+ # This class is copied (almost directly) from lib/instance_metadata.rb
46
+ # It is not loaded as the InstanceMetadata makes additional assumptions
47
+ # about the runtime that cannot be satisfied at install time, hence the
48
+ # trimmed copy.
49
+ class IMDS
50
+ IP_ADDRESS = '169.254.169.254'
51
+ TOKEN_PATH = '/latest/api/token'
52
+ BASE_PATH = '/latest/meta-data'
53
+ IDENTITY_DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'
54
+ DOMAIN_PATH = '/latest/meta-data/services/domain'
55
+
56
+ def self . imds_supported?
57
+ imds_v2? || imds_v1?
58
+ end
59
+
60
+ def self . imds_v1?
61
+ begin
62
+ get_request ( BASE_PATH ) { |response |
63
+ return response . kind_of? Net ::HTTPSuccess
64
+ }
65
+ rescue
66
+ false
67
+ end
68
+ end
69
+
70
+ def self . imds_v2?
71
+ begin
72
+ put_request ( TOKEN_PATH ) { |token_response |
73
+ ( token_response . kind_of? Net ::HTTPSuccess ) && get_request ( BASE_PATH , token_response . body ) { |response |
74
+ return response . kind_of? Net ::HTTPSuccess
75
+ }
76
+ }
77
+ rescue
78
+ false
79
+ end
80
+ end
81
+
82
+ def self . region
83
+ begin
84
+ identity_document ( ) [ 'region' ] . strip
85
+ rescue
86
+ nil
87
+ end
88
+ end
89
+
90
+ def self . domain
91
+ begin
92
+ get_instance_metadata ( DOMAIN_PATH ) . strip
93
+ rescue
94
+ nil
95
+ end
96
+ end
97
+
98
+ def self . identity_document
99
+ # JSON is lazy loaded to ensure we dont break older ruby runtimes
100
+ require 'json'
101
+ JSON . parse ( get_instance_metadata ( IDENTITY_DOCUMENT_PATH ) . strip )
102
+ end
103
+
104
+ private
105
+ def self . get_instance_metadata ( path )
106
+ begin
107
+ token = put_request ( TOKEN_PATH )
108
+ get_request ( path , token )
109
+ rescue
110
+ get_request ( path )
111
+ end
112
+ end
113
+
114
+
115
+ private
116
+ def self . http_request ( request )
117
+ Net ::HTTP . start ( IP_ADDRESS , 80 , :read_timeout => 10 , :open_timeout => 10 ) do |http |
118
+ response = http . request ( request )
119
+ if block_given?
120
+ yield ( response )
121
+ elsif response . kind_of? Net ::HTTPSuccess
122
+ response . body
123
+ else
124
+ raise "HTTP error from metadata service: #{ response . message } , code #{ response . code } "
125
+ end
126
+ end
127
+ end
128
+
129
+ def self . put_request ( path , &block )
130
+ request = Net ::HTTP ::Put . new ( path )
131
+ request [ 'X-aws-ec2-metadata-token-ttl-seconds' ] = '21600'
132
+ http_request ( request , &block )
133
+ end
134
+
135
+ def self . get_request ( path , token = nil , &block )
136
+ request = Net ::HTTP ::Get . new ( path )
137
+ unless token . nil?
138
+ request [ 'X-aws-ec2-metadata-token' ] = token
139
+ end
140
+ http_request ( request , &block )
141
+ end
142
+ end
143
+
144
+ class S3Bucket
145
+ # Split out as older versions of ruby dont like multi entry attr
146
+ attr :domain
147
+ attr :region
148
+ attr :bucket
149
+ def initialize ( domain , region , bucket )
150
+ @domain = domain
151
+ @region = region
152
+ @bucket = bucket
153
+ end
154
+
155
+ def object_uri ( object_key )
156
+ URI . parse ( "https://#{ @bucket } .s3.#{ @region } .#{ @domain } /#{ object_key } " )
157
+ end
158
+ end
159
+
45
160
begin
46
161
require 'fileutils'
47
162
require 'openssl'
151
266
@sanity_check = true
152
267
when '--help'
153
268
usage
269
+ exit ( 0 )
154
270
when '--re-execed'
155
271
@reexeced = true
156
272
when '--proxy'
@@ -187,13 +303,19 @@ EOF
187
303
end
188
304
end
189
305
306
+ parse_args ( )
307
+
308
+ # Be helpful when 'help' was used but not '--help'
309
+ if @type == 'help'
310
+ usage
311
+ exit ( 0 )
312
+ end
313
+
190
314
if ( Process . uid != 0 )
191
315
@log . error ( 'Must run as root to install packages' )
192
316
exit ( 1 )
193
317
end
194
318
195
- parse_args ( )
196
-
197
319
########## Force running as Ruby 2.x or fail here ##########
198
320
ruby_interpreter_path = check_ruby_version_and_symlink
199
321
force_ruby2x ( ruby_interpreter_path )
@@ -206,16 +328,17 @@ EOF
206
328
return exit_ok
207
329
end
208
330
209
- def get_ec2_metadata_region
210
- begin
211
- uri = URI . parse ( 'http://169.254.169.254/latest/dynamic/instance-identity/document' )
212
- document_string = uri . read ( :read_timeout => 120 )
213
- doc = JSON . parse ( document_string . strip )
214
- return doc [ 'region' ] . strip
215
- rescue
216
- @log . warn ( "Could not get region from EC2 metadata service at ' #{ uri . to_s } '" )
217
- return nil
331
+ def get_ec2_metadata_property ( property )
332
+ if IMDS . imds_supported?
333
+ begin
334
+ return IMDS . send ( property )
335
+ rescue => error
336
+ @log . warn ( "Could not get #{ property } from EC2 metadata service at ' #{ error . message } '" )
337
+ end
338
+ else
339
+ @log . warn ( "EC2 metadata service unavailable..." )
218
340
end
341
+ return nil
219
342
end
220
343
221
344
def get_region
@@ -224,27 +347,36 @@ EOF
224
347
return region if region
225
348
226
349
@log . info ( 'Checking EC2 metadata service for region information...' )
227
- region = get_ec2_metadata_region
350
+ region = get_ec2_metadata_property ( :region )
228
351
return region if region
229
352
230
353
@log . info ( 'Using fail-safe default region: us-east-1' )
231
354
return 'us-east-1'
232
355
end
233
356
234
- def get_s3_uri ( region , bucket , key )
235
- if ( region == 'us-east-1' )
236
- URI . parse ( "https://#{ bucket } .s3.amazonaws.com/#{ key } " )
237
- elsif ( region . split ( "-" ) [ 0 ] == 'cn' )
238
- URI . parse ( "https://#{ bucket } .s3.#{ region } .amazonaws.com.cn/#{ key } " )
239
- else
240
- URI . parse ( "https://#{ bucket } .s3.#{ region } .amazonaws.com/#{ key } " )
357
+ def get_domain ( fallback_region = nil )
358
+ @log . info ( 'Checking AWS_DOMAIN environment variable for domain information...' )
359
+ domain = ENV [ 'AWS_DOMAIN' ]
360
+ return domain if domain
361
+
362
+ @log . info ( 'Checking EC2 metadata service for domain information...' )
363
+ domain = get_ec2_metadata_property ( :domain )
364
+ return domain if domain
365
+
366
+ domain = 'amazonaws.com'
367
+ if !fallback_region . nil? && fallback_region . split ( "-" ) [ 0 ] == 'cn'
368
+ domain = 'amazonaws.com.cn'
241
369
end
370
+
371
+ @log . info ( "Using fail-safe default domain: #{ domain } " )
372
+ return domain
242
373
end
243
374
244
- def get_package_from_s3 ( region , bucket , key , package_file )
245
- @log . info ( "Downloading package from bucket #{ bucket } and key #{ key } ..." )
375
+ def get_package_from_s3 ( s3_bucket , key , package_file )
376
+ @log . info ( "Downloading package from bucket #{ s3_bucket . bucket } and key #{ key } ..." )
246
377
247
- uri = get_s3_uri ( region , bucket , key )
378
+ uri = s3_bucket . object_uri ( key )
379
+ @log . info ( "Endpoint: #{ uri } " )
248
380
249
381
# stream package file to disk
250
382
retries ||= 0
@@ -266,10 +398,11 @@ EOF
266
398
end
267
399
end
268
400
269
- def get_version_file_from_s3 ( region , bucket , key )
270
- @log . info ( "Downloading version file from bucket #{ bucket } and key #{ key } ..." )
401
+ def get_version_file_from_s3 ( s3_bucket , key )
402
+ @log . info ( "Downloading version file from bucket #{ s3_bucket . bucket } and key #{ key } ..." )
271
403
272
- uri = get_s3_uri ( region , bucket , key )
404
+ uri = s3_bucket . object_uri ( key )
405
+ @log . info ( "Endpoint: #{ uri } " )
273
406
274
407
begin
275
408
require 'json'
@@ -282,13 +415,13 @@ end
282
415
end
283
416
end
284
417
285
- def install_from_s3 ( region , bucket , package_key , install_cmd )
418
+ def install_from_s3 ( s3_bucket , package_key , install_cmd )
286
419
package_base_name = File . basename ( package_key )
287
420
package_extension = File . extname ( package_base_name )
288
421
package_name = File . basename ( package_base_name , package_extension )
289
422
package_file = Tempfile . new ( [ "#{ package_name } .tmp-" , package_extension ] ) # unique file with 0600 permissions
290
423
291
- get_package_from_s3 ( region , bucket , package_key , package_file )
424
+ get_package_from_s3 ( s3_bucket , package_key , package_file )
292
425
package_file . close
293
426
294
427
install_cmd << package_file . path
@@ -315,10 +448,10 @@ end
315
448
end
316
449
end
317
450
318
- def get_target_version ( target_version , type , region , bucket )
451
+ def get_target_version ( target_version , type , s3_bucket )
319
452
if target_version . nil?
320
453
version_file_key = 'latest/LATEST_VERSION'
321
- version_data = get_version_file_from_s3 ( region , bucket , version_file_key )
454
+ version_data = get_version_file_from_s3 ( s3_bucket , version_file_key )
322
455
if version_data . include? type
323
456
return version_data [ type ]
324
457
else
@@ -365,14 +498,14 @@ end
365
498
end
366
499
end
367
500
368
- region = get_region
501
+ region = get_region ( )
502
+ domain = get_domain ( region )
369
503
bucket = "aws-codedeploy-#{ region } "
504
+ s3_bucket = S3Bucket . new ( domain , region , bucket )
370
505
371
- target_version = get_target_version ( @target_version_arg , @type , region , bucket )
506
+ target_version = get_target_version ( @target_version_arg , @type , s3_bucket )
372
507
373
508
case @type
374
- when 'help'
375
- usage
376
509
when 'rpm'
377
510
running_version = `rpm -q codedeploy-agent`
378
511
running_version . strip!
381
514
else
382
515
#use -y to answer yes to confirmation prompts
383
516
install_cmd = [ '/usr/bin/yum' , '-y' , 'localinstall' ]
384
- install_from_s3 ( region , bucket , target_version , install_cmd )
517
+ install_from_s3 ( s3_bucket , target_version , install_cmd )
385
518
do_sanity_check ( '/sbin/service' )
386
519
end
387
520
when 'deb'
@@ -400,13 +533,13 @@ end
400
533
#use -n for non-interactive mode
401
534
#use -o to not overwrite config files unless they have not been changed
402
535
install_cmd = [ '/usr/bin/gdebi' , '-n' , '-o' , 'Dpkg::Options::=--force-confdef' , '-o' , 'Dkpg::Options::=--force-confold' ]
403
- install_from_s3 ( region , bucket , target_version , install_cmd )
536
+ install_from_s3 ( s3_bucket , target_version , install_cmd )
404
537
do_sanity_check ( '/usr/sbin/service' )
405
538
end
406
539
when 'zypper'
407
540
#use -n for non-interactive mode
408
541
install_cmd = [ '/usr/bin/zypper' , 'install' , '-n' ]
409
- install_from_s3 ( region , bucket , target_version , install_cmd )
542
+ install_from_s3 ( s3_bucket , target_version , install_cmd )
410
543
else
411
544
@log . error ( "Unsupported package type '#{ @type } '" )
412
545
exit ( 1 )
0 commit comments