Skip to content

Release v1.2.1 #271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ codedeploy-local.*.log
deployment/
.idea/
.DS_STORE
*.iml
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ group :test do
gem 'fakefs', :require => 'fakefs/safe'
gem 'mocha'
gem 'rspec'
gem 'webmock', :require => 'webmock/rspec'
gem 'shoulda'
gem 'shoulda-matchers'
gem 'shoulda-context'
Expand Down
55 changes: 49 additions & 6 deletions bin/install
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,52 @@ end

@log.level = Logger::INFO

require 'net/http'
require 'json'

TOKEN_PATH = '/latest/api/token'
DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'

class IMDSV2
def self.region
doc['region'].strip
end

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

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

def self.get_request(path, token = nil)
request = Net::HTTP::Get.new(path)
unless token.nil?
request['X-aws-ec2-metadata-token'] = token
end
http_request(request)
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
end
end

begin
require 'fileutils'
require 'openssl'
Expand Down Expand Up @@ -208,12 +254,9 @@ EOF

def get_ec2_metadata_region
begin
uri = URI.parse('http://169.254.169.254/latest/dynamic/instance-identity/document')
document_string = uri.read(:read_timeout => 120)
doc = JSON.parse(document_string.strip)
return doc['region'].strip
rescue
@log.warn("Could not get region from EC2 metadata service at '#{uri.to_s}'")
return IMDSV2.region
rescue => error
@log.warn("Could not get region from EC2 metadata service at '#{error.message}'")
return nil
end
end
Expand Down
55 changes: 49 additions & 6 deletions bin/update
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,52 @@ end

@log.level = Logger::INFO

require 'net/http'
require 'json'

TOKEN_PATH = '/latest/api/token'
DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'

class IMDSV2
def self.region
doc['region'].strip
end

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

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

def self.get_request(path, token = nil)
request = Net::HTTP::Get.new(path)
unless token.nil?
request['X-aws-ec2-metadata-token'] = token
end
http_request(request)
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
end
end

require 'set'
VALID_TYPES = Set.new ['rpm','zypper','deb','msi']

Expand Down Expand Up @@ -275,12 +321,9 @@ EOF

def get_ec2_metadata_region
begin
uri = URI.parse('http://169.254.169.254/latest/dynamic/instance-identity/document')
document_string = uri.read(:read_timeout => 120)
doc = JSON.parse(document_string.strip)
return doc['region'].strip
rescue
@log.warn("Could not get region from EC2 metadata service at '#{uri.to_s}'")
return IMDSV2.region
rescue => error
@log.warn("Could not get region from EC2 metadata service at '#{error.message}'")
return nil
end
end
Expand Down
10 changes: 6 additions & 4 deletions codedeploy_agent.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = 'aws_codedeploy_agent'
spec.version = '1.1.2'
spec.version = '1.2.1'
spec.summary = 'Packages AWS CodeDeploy agent libraries'
spec.description = 'AWS CodeDeploy agent is responsible for doing the actual work of deploying software on an individual EC2 instance'
spec.author = 'Amazon Web Services'
Expand All @@ -14,13 +14,15 @@ Gem::Specification.new do |spec|
spec.add_dependency('gli', '~> 2.5')
spec.add_dependency('json_pure', '~> 1.6')
spec.add_dependency('archive-tar-minitar', '~> 0.5.2')
spec.add_dependency('rubyzip', '~> 1.1.0')
spec.add_dependency('rubyzip', '~> 1.3.0')
spec.add_dependency('logging', '~> 1.8')
spec.add_dependency('aws-sdk-core', '~> 2.9')
spec.add_dependency('aws-sdk-core', '~> 3')
spec.add_dependency('aws-sdk-code-generator', '~> 0.2.2.pre')
spec.add_dependency('aws-sdk-s3', '~> 1')
spec.add_dependency('simple_pid', '~> 0.2.1')
spec.add_dependency('docopt', '~> 0.5.0')
spec.add_dependency('concurrent-ruby', '~> 1.0.5')

spec.add_development_dependency('rake', '~> 10.0')
spec.add_development_dependency('rake', '~> 12.3.3')
spec.add_development_dependency('rspec', '~> 3.2.0')
end
1 change: 1 addition & 0 deletions features/step_definitions/common_steps.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'aws-sdk-core'
require 'aws-sdk-s3'

$:.unshift File.join(File.dirname(File.expand_path('../..', __FILE__)), 'features')
require 'step_definitions/step_constants'
Expand Down
3 changes: 2 additions & 1 deletion lib/instance_agent/file_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def initialize(path)
private

def refresh
@credentials = Aws::SharedCredentials.new(path: @path)
@credentials = Aws::SharedCredentials.new(path: @path).credentials
raise "Failed to load credentials from path #{@path}" if @credentials.nil?
@expiration = Time.new + 1800
end
end
Expand Down
6 changes: 4 additions & 2 deletions lib/instance_agent/plugins/codedeploy/command_executor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'openssl'
require 'fileutils'
require 'aws-sdk-core'
require 'aws-sdk-s3'
require 'zlib'
require 'zip'
require 'instance_metadata'
Expand All @@ -12,6 +13,7 @@
require 'instance_agent/plugins/codedeploy/deployment_specification'
require 'instance_agent/plugins/codedeploy/hook_executor'
require 'instance_agent/plugins/codedeploy/installer'
require 'instance_agent/string_utils'

module InstanceAgent
module Plugins
Expand Down Expand Up @@ -47,8 +49,8 @@ def initialize(options = {})

def self.command(name, &blk)
@command_methods ||= Hash.new

method = Seahorse::Util.underscore(name).to_sym
raise "Received command is not in PascalCase form: #{name.to_s}" unless StringUtils.is_pascal_case(name.to_s)
method = StringUtils.underscore(name.to_s)
@command_methods[name] = method

define_method(method, &blk)
Expand Down
16 changes: 16 additions & 0 deletions lib/instance_agent/string_utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module InstanceAgent
class StringUtils

def self.underscore(string)
string.
gsub(/([A-Z0-9]+)([A-Z][a-z])/, '\1_\2').
scan(/[a-z0-9]+|\d+|[A-Z0-9]+[a-z]*/).
join('_').downcase
end

def self.is_pascal_case(string)
!!(string =~ /^([A-Z][a-z0-9]+)+/)
end

end
end
60 changes: 44 additions & 16 deletions lib/instance_metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,26 @@ class InstanceMetadata
PORT = 80
HTTP_TIMEOUT = 30

PARTITION_PATH = '/latest/meta-data/services/partition'
INSTANCE_ID_PATH = '/latest/meta-data/instance-id'
TOKEN_PATH = '/latest/api/token'
DOCUMENT_PATH = '/latest/dynamic/instance-identity/document'

def self.host_identifier
"arn:#{partition}:ec2:#{doc['region']}:#{doc['accountId']}:instance/#{doc['instanceId']}"
end

def self.partition
http_get('/latest/meta-data/services/partition').strip
get_metadata_wrapper(PARTITION_PATH).strip
end

def self.region
doc['region']
doc['region'].strip
end

def self.instance_id
begin
Net::HTTP.start(IP_ADDRESS, PORT) do |http|
response = http.get('/latest/meta-data/instance-id')
if response.code.to_i != 200
return nil
end
return response.body
end
get_metadata_wrapper(INSTANCE_ID_PATH)
rescue
return nil
end
Expand All @@ -39,19 +38,48 @@ class InstanceMetadataError < StandardError
end

private
def self.http_get(path)
Net::HTTP.start(IP_ADDRESS, PORT, :read_timeout => HTTP_TIMEOUT/2, :open_timeout => HTTP_TIMEOUT/2) do |http|
response = http.get(path)
def self.get_metadata_wrapper(path)
begin
token = put_request(TOKEN_PATH)
get_request(path, token)
rescue
InstanceAgent::Log.send(:info, "IMDSv2 http request failed, falling back to IMDSv1.")
get_request(path)
end

end

def self.http_request(request)
Net::HTTP.start(IP_ADDRESS, PORT, :read_timeout => 120, :open_timeout => 120) do |http|
response = http.request(request)
if response.code.to_i != 200
InstanceAgent::Log.send(:debug, "HTTP error from metadata service, code #{response.code}")
raise "HTTP error from metadata service, code #{response.code}"
raise "HTTP error from metadata service: #{response.message}, code #{response.code}"
end
return response.body
end
end

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

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

def self.doc
JSON.parse(http_get('/latest/dynamic/instance-identity/document').strip)
begin
token = put_request(TOKEN_PATH)
JSON.parse(get_request(DOCUMENT_PATH, token).strip)
rescue
InstanceAgent::Log.send(:info, "IMDSv2 http request failed, falling back to IMDSv1.")
JSON.parse(get_request(DOCUMENT_PATH).strip)
end
end
end
63 changes: 63 additions & 0 deletions spec/add_service_wrapper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true
package_root = File.dirname(File.dirname(__FILE__))

require "#{package_root}/vendor/gems/codedeploy-commands-1.0.0/lib/aws/add_service_wrapper"

RSpec.describe 'add_service_wrapper' do

# This test is taken from the AwsSdkRubyCodeGenWrapper
# https://code.amazon.com/packages/AwsSdkRubyCodeGenWrapper/blobs/mainline/--/spec/add_service_wrapper_spec.rb
describe '#add_service' do
before(:all) do
@service_file = File.expand_path('../fixtures/sample_service.json', __FILE__)
@api = JSON.parse(File.read(@service_file))
@svc_class = Aws.add_service('GeneratedService', api: @api)
end

let(:client) {Aws::GeneratedService::Client.new(stub_responses: true) }

it 'can create a valid client' do
expect(client).to be_instance_of(Aws::GeneratedService::Client)
end

it 'can create a client from the returned namespace' do
expect(@svc_class::Client.new(stub_responses: true))
.to be_instance_of(Aws::GeneratedService::Client)
end

it 'can set constants on the returned namespace' do
@svc_class.const_set(:VERSION, '1.1.42')
expect(Aws::GeneratedService::VERSION).to eq('1.1.42')
end

it 'can add plugins to the generated client' do
class MyPlugin; end
Aws::GeneratedService::Client.add_plugin(MyPlugin)
expect(Aws::GeneratedService::Client.plugins).to include(MyPlugin)
end

it 'can generate a whitelabel (non-Aws) service' do
Aws.add_service('MyService', api: @api, whitelabel: true)
expect(MyService::Client.new(stub_responses: true))
.to be_instance_of(MyService::Client)
end

it 'loads the model from a string path' do
Aws.add_service('StringPathService', api: @service_file)
expect(Aws::StringPathService::Client.new(stub_responses: true))
.to be_instance_of(Aws::StringPathService::Client)
end

it 'loads the model from a PathName' do
Aws.add_service('PathService', api: Pathname.new(@service_file))
expect(Aws::PathService::Client.new(stub_responses: true))
.to be_instance_of(Aws::PathService::Client)
end

it 'raises an ArgumentError if api is not provided' do
expect do
Aws.add_service('NoApiService')
end.to raise_exception(ArgumentError)
end
end
end
Loading