Skip to content

Commit baaa837

Browse files
authored
Merge pull request #275 from dljvette/v1.3.1
V1.3.1 Release
2 parents 030bc3a + 1f52e69 commit baaa837

File tree

17 files changed

+652
-233
lines changed

17 files changed

+652
-233
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Please do the build steps mentioned above before running the integration test.
2828
The integration test creates the following
2929
* An IAM role "codedeploy-agent-integ-test-deployment-role" if it doesn't exist
3030
* An IAM role "codedeploy-agent-integ-test-instance-role" if it doesn't exist
31+
* An IAM user "codedeploy-agent-integ-test-instance-user" if it doesn't exist. (Access key will be recreated.)
3132
* A CodeDeploy application
3233
* Startup the codedeploy agent on your host
3334
* A CodeDeploy deployment group with your host in it

bin/install

+144-54
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
# than 2.0. Testing on multiple Ruby versions is required for
66
# changes to this part of the code.
77
##################################################################
8-
require 'json'
9-
108
class Proxy
119
instance_methods.each do |m|
1210
undef_method m unless m =~ /(^__|^send$|^object_id$)/
@@ -43,48 +41,119 @@ end
4341
@log.level = Logger::INFO
4442

4543
require 'net/http'
46-
require 'json'
4744

48-
TOKEN_PATH = '/latest/api/token'
49-
DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'
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
5081

51-
class IMDSV2
5282
def self.region
53-
doc['region'].strip
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)
54102
end
55103

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+
56115
private
57116
def self.http_request(request)
58-
Net::HTTP.start('169.254.169.254', 80, :read_timeout => 120, :open_timeout => 120) do |http|
117+
Net::HTTP.start(IP_ADDRESS, 80, :read_timeout => 10, :open_timeout => 10) do |http|
59118
response = http.request(request)
60-
if response.code.to_i != 200
119+
if block_given?
120+
yield(response)
121+
elsif response.kind_of? Net::HTTPSuccess
122+
response.body
123+
else
61124
raise "HTTP error from metadata service: #{response.message}, code #{response.code}"
62125
end
63-
return response.body
64126
end
65127
end
66128

67-
def self.put_request(path)
129+
def self.put_request(path, &block)
68130
request = Net::HTTP::Put.new(path)
69131
request['X-aws-ec2-metadata-token-ttl-seconds'] = '21600'
70-
http_request(request)
132+
http_request(request, &block)
71133
end
72134

73-
def self.get_request(path, token = nil)
135+
def self.get_request(path, token = nil, &block)
74136
request = Net::HTTP::Get.new(path)
75137
unless token.nil?
76138
request['X-aws-ec2-metadata-token'] = token
77139
end
78-
http_request(request)
140+
http_request(request, &block)
79141
end
142+
end
80143

81-
def self.doc
82-
begin
83-
token = put_request(TOKEN_PATH)
84-
JSON.parse(get_request(DOCUMENT_PATH, token).strip)
85-
rescue
86-
JSON.parse(get_request(DOCUMENT_PATH).strip)
87-
end
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}")
88157
end
89158
end
90159

@@ -197,6 +266,7 @@ EOF
197266
@sanity_check = true
198267
when '--help'
199268
usage
269+
exit(0)
200270
when '--re-execed'
201271
@reexeced = true
202272
when '--proxy'
@@ -233,13 +303,19 @@ EOF
233303
end
234304
end
235305

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+
236314
if (Process.uid != 0)
237315
@log.error('Must run as root to install packages')
238316
exit(1)
239317
end
240318

241-
parse_args()
242-
243319
########## Force running as Ruby 2.x or fail here ##########
244320
ruby_interpreter_path = check_ruby_version_and_symlink
245321
force_ruby2x(ruby_interpreter_path)
@@ -252,13 +328,17 @@ EOF
252328
return exit_ok
253329
end
254330

255-
def get_ec2_metadata_region
256-
begin
257-
return IMDSV2.region
258-
rescue => error
259-
@log.warn("Could not get region from EC2 metadata service at '#{error.message}'")
260-
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...")
261340
end
341+
return nil
262342
end
263343

264344
def get_region
@@ -267,27 +347,36 @@ EOF
267347
return region if region
268348

269349
@log.info('Checking EC2 metadata service for region information...')
270-
region = get_ec2_metadata_region
350+
region = get_ec2_metadata_property(:region)
271351
return region if region
272352

273353
@log.info('Using fail-safe default region: us-east-1')
274354
return 'us-east-1'
275355
end
276356

277-
def get_s3_uri(region, bucket, key)
278-
if (region == 'us-east-1')
279-
URI.parse("https://#{bucket}.s3.amazonaws.com/#{key}")
280-
elsif (region.split("-")[0] == 'cn')
281-
URI.parse("https://#{bucket}.s3.#{region}.amazonaws.com.cn/#{key}")
282-
else
283-
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'
284369
end
370+
371+
@log.info("Using fail-safe default domain: #{domain}")
372+
return domain
285373
end
286374

287-
def get_package_from_s3(region, bucket, key, package_file)
288-
@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}...")
289377

290-
uri = get_s3_uri(region, bucket, key)
378+
uri = s3_bucket.object_uri(key)
379+
@log.info("Endpoint: #{uri}")
291380

292381
# stream package file to disk
293382
retries ||= 0
@@ -309,10 +398,11 @@ EOF
309398
end
310399
end
311400

312-
def get_version_file_from_s3(region, bucket, key)
313-
@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}...")
314403

315-
uri = get_s3_uri(region, bucket, key)
404+
uri = s3_bucket.object_uri(key)
405+
@log.info("Endpoint: #{uri}")
316406

317407
begin
318408
require 'json'
@@ -325,13 +415,13 @@ end
325415
end
326416
end
327417

328-
def install_from_s3(region, bucket, package_key, install_cmd)
418+
def install_from_s3(s3_bucket, package_key, install_cmd)
329419
package_base_name = File.basename(package_key)
330420
package_extension = File.extname(package_base_name)
331421
package_name = File.basename(package_base_name, package_extension)
332422
package_file = Tempfile.new(["#{package_name}.tmp-", package_extension]) # unique file with 0600 permissions
333423

334-
get_package_from_s3(region, bucket, package_key, package_file)
424+
get_package_from_s3(s3_bucket, package_key, package_file)
335425
package_file.close
336426

337427
install_cmd << package_file.path
@@ -358,10 +448,10 @@ end
358448
end
359449
end
360450

361-
def get_target_version(target_version, type, region, bucket)
451+
def get_target_version(target_version, type, s3_bucket)
362452
if target_version.nil?
363453
version_file_key = 'latest/LATEST_VERSION'
364-
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)
365455
if version_data.include? type
366456
return version_data[type]
367457
else
@@ -408,14 +498,14 @@ end
408498
end
409499
end
410500

411-
region = get_region
501+
region = get_region()
502+
domain = get_domain(region)
412503
bucket = "aws-codedeploy-#{region}"
504+
s3_bucket = S3Bucket.new(domain, region, bucket)
413505

414-
target_version = get_target_version(@target_version_arg, @type, region, bucket)
506+
target_version = get_target_version(@target_version_arg, @type, s3_bucket)
415507

416508
case @type
417-
when 'help'
418-
usage
419509
when 'rpm'
420510
running_version = `rpm -q codedeploy-agent`
421511
running_version.strip!
@@ -424,7 +514,7 @@ end
424514
else
425515
#use -y to answer yes to confirmation prompts
426516
install_cmd = ['/usr/bin/yum', '-y', 'localinstall']
427-
install_from_s3(region, bucket, target_version, install_cmd)
517+
install_from_s3(s3_bucket, target_version, install_cmd)
428518
do_sanity_check('/sbin/service')
429519
end
430520
when 'deb'
@@ -443,13 +533,13 @@ end
443533
#use -n for non-interactive mode
444534
#use -o to not overwrite config files unless they have not been changed
445535
install_cmd = ['/usr/bin/gdebi', '-n', '-o', 'Dpkg::Options::=--force-confdef', '-o', 'Dkpg::Options::=--force-confold']
446-
install_from_s3(region, bucket, target_version, install_cmd)
536+
install_from_s3(s3_bucket, target_version, install_cmd)
447537
do_sanity_check('/usr/sbin/service')
448538
end
449539
when 'zypper'
450540
#use -n for non-interactive mode
451541
install_cmd = ['/usr/bin/zypper', 'install', '-n']
452-
install_from_s3(region, bucket, target_version, install_cmd)
542+
install_from_s3(s3_bucket, target_version, install_cmd)
453543
else
454544
@log.error("Unsupported package type '#{@type}'")
455545
exit(1)

0 commit comments

Comments
 (0)