Skip to content

Commit

Permalink
Merge pull request #275 from dljvette/v1.3.1
Browse files Browse the repository at this point in the history
V1.3.1 Release
  • Loading branch information
vrr-21 authored Jan 8, 2021
2 parents 030bc3a + 1f52e69 commit baaa837
Show file tree
Hide file tree
Showing 17 changed files with 652 additions and 233 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Please do the build steps mentioned above before running the integration test.
The integration test creates the following
* An IAM role "codedeploy-agent-integ-test-deployment-role" if it doesn't exist
* An IAM role "codedeploy-agent-integ-test-instance-role" if it doesn't exist
* An IAM user "codedeploy-agent-integ-test-instance-user" if it doesn't exist. (Access key will be recreated.)
* A CodeDeploy application
* Startup the codedeploy agent on your host
* A CodeDeploy deployment group with your host in it
Expand Down
198 changes: 144 additions & 54 deletions bin/install
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
# than 2.0. Testing on multiple Ruby versions is required for
# changes to this part of the code.
##################################################################
require 'json'

class Proxy
instance_methods.each do |m|
undef_method m unless m =~ /(^__|^send$|^object_id$)/
Expand Down Expand Up @@ -43,48 +41,119 @@ end
@log.level = Logger::INFO

require 'net/http'
require 'json'

TOKEN_PATH = '/latest/api/token'
DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'
# This class is copied (almost directly) from lib/instance_metadata.rb
# It is not loaded as the InstanceMetadata makes additional assumptions
# about the runtime that cannot be satisfied at install time, hence the
# trimmed copy.
class IMDS
IP_ADDRESS = '169.254.169.254'
TOKEN_PATH = '/latest/api/token'
BASE_PATH = '/latest/meta-data'
IDENTITY_DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'
DOMAIN_PATH = '/latest/meta-data/services/domain'

def self.imds_supported?
imds_v2? || imds_v1?
end

def self.imds_v1?
begin
get_request(BASE_PATH) { |response|
return response.kind_of? Net::HTTPSuccess
}
rescue
false
end
end

def self.imds_v2?
begin
put_request(TOKEN_PATH) { |token_response|
(token_response.kind_of? Net::HTTPSuccess) && get_request(BASE_PATH, token_response.body) { |response|
return response.kind_of? Net::HTTPSuccess
}
}
rescue
false
end
end

class IMDSV2
def self.region
doc['region'].strip
begin
identity_document()['region'].strip
rescue
nil
end
end

def self.domain
begin
get_instance_metadata(DOMAIN_PATH).strip
rescue
nil
end
end

def self.identity_document
# JSON is lazy loaded to ensure we dont break older ruby runtimes
require 'json'
JSON.parse(get_instance_metadata(IDENTITY_DOCUMENT_PATH).strip)
end

private
def self.get_instance_metadata(path)
begin
token = put_request(TOKEN_PATH)
get_request(path, token)
rescue
get_request(path)
end
end


private
def self.http_request(request)
Net::HTTP.start('169.254.169.254', 80, :read_timeout => 120, :open_timeout => 120) do |http|
Net::HTTP.start(IP_ADDRESS, 80, :read_timeout => 10, :open_timeout => 10) do |http|
response = http.request(request)
if response.code.to_i != 200
if block_given?
yield(response)
elsif response.kind_of? Net::HTTPSuccess
response.body
else
raise "HTTP error from metadata service: #{response.message}, code #{response.code}"
end
return response.body
end
end

def self.put_request(path)
def self.put_request(path, &block)
request = Net::HTTP::Put.new(path)
request['X-aws-ec2-metadata-token-ttl-seconds'] = '21600'
http_request(request)
http_request(request, &block)
end

def self.get_request(path, token = nil)
def self.get_request(path, token = nil, &block)
request = Net::HTTP::Get.new(path)
unless token.nil?
request['X-aws-ec2-metadata-token'] = token
end
http_request(request)
http_request(request, &block)
end
end

def self.doc
begin
token = put_request(TOKEN_PATH)
JSON.parse(get_request(DOCUMENT_PATH, token).strip)
rescue
JSON.parse(get_request(DOCUMENT_PATH).strip)
end
class S3Bucket
# Split out as older versions of ruby dont like multi entry attr
attr :domain
attr :region
attr :bucket
def initialize(domain, region, bucket)
@domain = domain
@region = region
@bucket = bucket
end

def object_uri(object_key)
URI.parse("https://#{@bucket}.s3.#{@region}.#{@domain}/#{object_key}")
end
end

Expand Down Expand Up @@ -197,6 +266,7 @@ EOF
@sanity_check = true
when '--help'
usage
exit(0)
when '--re-execed'
@reexeced = true
when '--proxy'
Expand Down Expand Up @@ -233,13 +303,19 @@ EOF
end
end

parse_args()

# Be helpful when 'help' was used but not '--help'
if @type == 'help'
usage
exit(0)
end

if (Process.uid != 0)
@log.error('Must run as root to install packages')
exit(1)
end

parse_args()

########## Force running as Ruby 2.x or fail here ##########
ruby_interpreter_path = check_ruby_version_and_symlink
force_ruby2x(ruby_interpreter_path)
Expand All @@ -252,13 +328,17 @@ EOF
return exit_ok
end

def get_ec2_metadata_region
begin
return IMDSV2.region
rescue => error
@log.warn("Could not get region from EC2 metadata service at '#{error.message}'")
return nil
def get_ec2_metadata_property(property)
if IMDS.imds_supported?
begin
return IMDS.send(property)
rescue => error
@log.warn("Could not get #{property} from EC2 metadata service at '#{error.message}'")
end
else
@log.warn("EC2 metadata service unavailable...")
end
return nil
end

def get_region
Expand All @@ -267,27 +347,36 @@ EOF
return region if region

@log.info('Checking EC2 metadata service for region information...')
region = get_ec2_metadata_region
region = get_ec2_metadata_property(:region)
return region if region

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

def get_s3_uri(region, bucket, key)
if (region == 'us-east-1')
URI.parse("https://#{bucket}.s3.amazonaws.com/#{key}")
elsif (region.split("-")[0] == 'cn')
URI.parse("https://#{bucket}.s3.#{region}.amazonaws.com.cn/#{key}")
else
URI.parse("https://#{bucket}.s3.#{region}.amazonaws.com/#{key}")
def get_domain(fallback_region = nil)
@log.info('Checking AWS_DOMAIN environment variable for domain information...')
domain = ENV['AWS_DOMAIN']
return domain if domain

@log.info('Checking EC2 metadata service for domain information...')
domain = get_ec2_metadata_property(:domain)
return domain if domain

domain = 'amazonaws.com'
if !fallback_region.nil? && fallback_region.split("-")[0] == 'cn'
domain = 'amazonaws.com.cn'
end

@log.info("Using fail-safe default domain: #{domain}")
return domain
end

def get_package_from_s3(region, bucket, key, package_file)
@log.info("Downloading package from bucket #{bucket} and key #{key}...")
def get_package_from_s3(s3_bucket, key, package_file)
@log.info("Downloading package from bucket #{s3_bucket.bucket} and key #{key}...")

uri = get_s3_uri(region, bucket, key)
uri = s3_bucket.object_uri(key)
@log.info("Endpoint: #{uri}")

# stream package file to disk
retries ||= 0
Expand All @@ -309,10 +398,11 @@ EOF
end
end

def get_version_file_from_s3(region, bucket, key)
@log.info("Downloading version file from bucket #{bucket} and key #{key}...")
def get_version_file_from_s3(s3_bucket, key)
@log.info("Downloading version file from bucket #{s3_bucket.bucket} and key #{key}...")

uri = get_s3_uri(region, bucket, key)
uri = s3_bucket.object_uri(key)
@log.info("Endpoint: #{uri}")

begin
require 'json'
Expand All @@ -325,13 +415,13 @@ end
end
end

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

get_package_from_s3(region, bucket, package_key, package_file)
get_package_from_s3(s3_bucket, package_key, package_file)
package_file.close

install_cmd << package_file.path
Expand All @@ -358,10 +448,10 @@ end
end
end

def get_target_version(target_version, type, region, bucket)
def get_target_version(target_version, type, s3_bucket)
if target_version.nil?
version_file_key = 'latest/LATEST_VERSION'
version_data = get_version_file_from_s3(region, bucket, version_file_key)
version_data = get_version_file_from_s3(s3_bucket, version_file_key)
if version_data.include? type
return version_data[type]
else
Expand Down Expand Up @@ -408,14 +498,14 @@ end
end
end

region = get_region
region = get_region()
domain = get_domain(region)
bucket = "aws-codedeploy-#{region}"
s3_bucket = S3Bucket.new(domain, region, bucket)

target_version = get_target_version(@target_version_arg, @type, region, bucket)
target_version = get_target_version(@target_version_arg, @type, s3_bucket)

case @type
when 'help'
usage
when 'rpm'
running_version = `rpm -q codedeploy-agent`
running_version.strip!
Expand All @@ -424,7 +514,7 @@ end
else
#use -y to answer yes to confirmation prompts
install_cmd = ['/usr/bin/yum', '-y', 'localinstall']
install_from_s3(region, bucket, target_version, install_cmd)
install_from_s3(s3_bucket, target_version, install_cmd)
do_sanity_check('/sbin/service')
end
when 'deb'
Expand All @@ -443,13 +533,13 @@ end
#use -n for non-interactive mode
#use -o to not overwrite config files unless they have not been changed
install_cmd = ['/usr/bin/gdebi', '-n', '-o', 'Dpkg::Options::=--force-confdef', '-o', 'Dkpg::Options::=--force-confold']
install_from_s3(region, bucket, target_version, install_cmd)
install_from_s3(s3_bucket, target_version, install_cmd)
do_sanity_check('/usr/sbin/service')
end
when 'zypper'
#use -n for non-interactive mode
install_cmd = ['/usr/bin/zypper', 'install', '-n']
install_from_s3(region, bucket, target_version, install_cmd)
install_from_s3(s3_bucket, target_version, install_cmd)
else
@log.error("Unsupported package type '#{@type}'")
exit(1)
Expand Down
Loading

0 comments on commit baaa837

Please sign in to comment.