Skip to content
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

WIP: Config and credentials files parsing and refactor #3064

Closed
wants to merge 4 commits into from
Closed
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
14 changes: 2 additions & 12 deletions gems/aws-sdk-core/lib/aws-sdk-core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
require_relative 'aws-sdk-core/deprecations'

# credential providers

require_relative 'aws-sdk-core/profile'
require_relative 'aws-sdk-core/credential_provider'
require_relative 'aws-sdk-core/refreshing_credentials'
require_relative 'aws-sdk-core/assume_role_credentials'
Expand All @@ -30,7 +30,6 @@
require_relative 'aws-sdk-core/plugins/bearer_authorization'

# client modules

require_relative 'aws-sdk-core/client_stubs'
require_relative 'aws-sdk-core/async_client_stubs'
require_relative 'aws-sdk-core/eager_loader'
Expand All @@ -45,24 +44,20 @@
require_relative 'aws-sdk-core/util'

# resource classes

require_relative 'aws-sdk-core/resources/collection'

# logging

require_relative 'aws-sdk-core/log/formatter'
require_relative 'aws-sdk-core/log/param_filter'
require_relative 'aws-sdk-core/log/param_formatter'

# stubbing

require_relative 'aws-sdk-core/stubbing/empty_stub'
require_relative 'aws-sdk-core/stubbing/data_applicator'
require_relative 'aws-sdk-core/stubbing/stub_data'
require_relative 'aws-sdk-core/stubbing/xml_error'

# stubbing protocols

require_relative 'aws-sdk-core/stubbing/protocols/json'
require_relative 'aws-sdk-core/stubbing/protocols/rest'
require_relative 'aws-sdk-core/stubbing/protocols/rest_json'
Expand All @@ -73,7 +68,6 @@
require_relative 'aws-sdk-core/stubbing/protocols/api_gateway'

# protocols

require_relative 'aws-sdk-core/error_handler'
require_relative 'aws-sdk-core/rest'
require_relative 'aws-sdk-core/xml'
Expand All @@ -82,29 +76,25 @@
require_relative 'aws-sdk-core/rpc_v2'

# event stream

require_relative 'aws-sdk-core/binary'
require_relative 'aws-sdk-core/event_emitter'

# endpoint discovery

require_relative 'aws-sdk-core/endpoint_cache'

# client metrics

require_relative 'aws-sdk-core/client_side_monitoring/request_metrics'
require_relative 'aws-sdk-core/client_side_monitoring/publisher'

# utilities

require_relative 'aws-sdk-core/arn'
require_relative 'aws-sdk-core/arn_parser'
require_relative 'aws-sdk-core/ec2_metadata'
require_relative 'aws-sdk-core/lru_cache'

# dynamic endpoints
require_relative 'aws-sdk-core/endpoints'
require_relative 'aws-sdk-core/plugins/signature_v4'
require_relative 'aws-sdk-core/plugins/signature_v4' # deprecated

# defaults
require_relative 'aws-defaults'
Expand Down
8 changes: 8 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/profile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

require_relative 'profile/profile'
require_relative 'profile/profile_file'
require_relative 'profile/profile_file_factory'
require_relative 'profile/profile_file_parser'
require_relative 'profile/profile_file_standardizer'
require_relative 'profile/profile_file_utils'
67 changes: 67 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/profile/profile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module Aws
# @api private
class Profile
def initialize(name, properties = {})
@name = name
@properties = {}
update_properties(properties)
end

attr_reader :name, :properties

def update_properties(new_values)
new_values.each do |key, value|
if @properties.key?(key)
puts "Warning: Duplicate property '#{key}' detected in profile #{@name}. "\
'One value will be ignored.'
end
@properties[key] = value
end
end

# @api private
class Property
attr_reader :name, :value, :sub_properties

def initialize(name, value)
@name = name
@value = value
@sub_properties = parse_sub_properties
end

def to_h
if @sub_properties
@sub_properties.transform_values(&:to_h)
else
@value
end
end

private

def parse_sub_properties
return nil unless @value.start_with?("\n")

sub_properties = {}
@value.split(/[\r\n]+/).each do |raw_sub_property_line|
next if ProfileFileUtils.empty_line?(raw_sub_property_line)

left, right = ProfileFileUtils.parse_property_definition_line(
raw_sub_property_line, "in sub-property of #{@name}"
)
next unless ProfileFileUtils.valid_identifier?(left)

if sub_properties.key?(left)
puts "Warning: Duplicate sub-property '#{left}' detected in property '#{@name}'. "\
'The later one in the file will be used.'
end

sub_properties[left] = Property.new(left, right)
end
sub_properties
end
end
end
end
67 changes: 67 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/profile/profile_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module Aws
# @api private
class ProfileFile
def initialize(config_profiles, credentials_profiles)
@profiles = merge_files(config_profiles, credentials_profiles)
.transform_values { |profile| profile.properties.to_h }
end

attr_reader :profiles

# def credentials(profile_name)
# return nil unless @profiles.key?(profile_name)
#
# properties = @profiles[profile_name].properties
#
# if properties.key?('role_arn')
# return AssumeRoleCredentials.new(profile_name, @profiles)
# elsif properties.key?('aws_access_key_id')
# aws_access_key_id = properties['aws_access_key_id']
# aws_secret_access_key = properties['aws_secret_access_key']
# aws_session_token = properties['aws_session_token']
#
# raise "'aws_secret_access_key' was not specified in profile: #{profile_name}" unless aws_secret_access_key
#
# if aws_session_token
# return SessionCredentials.new(aws_access_key_id.value, aws_secret_access_key.value, aws_session_token.value)
# else
# return BasicCredentials.new(aws_access_key_id.value, aws_secret_access_key.value)
# end
# else
# return nil
# end
# end
#
# def region(profile_name)
# return nil unless @profiles.key?(profile_name)
#
# region = @profiles[profile_name].properties['region']
#
# return nil unless region
#
# region.value
# end

private

def merge_files(config_file, credentials_file)
aggregate_file = config_file.dup

credentials_file.each do |credentials_profile_name, credentials_profile|
if !aggregate_file.key?(credentials_profile_name)
aggregate_file[credentials_profile_name] = credentials_profile
else
puts "Warning: The profile '#{credentials_profile_name}' was found in both the configuration and " \
"credentials configuration files. The properties will be merged, using the property in the credentials " \
"file if there are duplicates."

aggregate_file[credentials_profile_name].update_properties(credentials_profile.properties)
end
end

aggregate_file
end
end
end
58 changes: 58 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/profile/profile_file_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

module Aws
# @api private
class ProfileFileFactory
def create
raw_config_file = ProfileFileParser.new(configuration_file_contents).parse
raw_credentials_file = ProfileFileParser.new(credentials_file_contents).parse

standardized_config_file = ProfileFileStandardizer.new(raw_config_file, :config).standardize
standardized_credentials_file = ProfileFileStandardizer.new(raw_credentials_file, :credentials).standardize

ProfileFile.new(standardized_config_file, standardized_credentials_file)
end

private

def configuration_file_contents
load_file('AWS_CONFIG_FILE', File.join(user_home_directory, '.aws', 'config'))
end

def credentials_file_contents
load_file('AWS_SHARED_CREDENTIALS_FILE', File.join(user_home_directory, '.aws', 'credentials'))
end

def load_file(file_environment_variable, default_file_location)
file_location = ENV[file_environment_variable]

if file_location.nil?
file_location = File.expand_path(default_file_location)
end

if file_location =~ /^~(\/|#{Regexp.quote(File::SEPARATOR)}).*$/
file_location = user_home_directory + file_location[1..-1]
end

configuration_file = Pathname.new(file_location)

return '' unless configuration_file.readable?

configuration_file.read
end

def user_home_directory
require 'byebug'
byebug
if ENV['HOME']
ENV['HOME']
elsif ENV['USERPROFILE']
ENV['USERPROFILE']
elsif ENV['HOMEDRIVE'] && ENV['HOMEPATH']
ENV['HOMEDRIVE'] + ENV['HOMEPATH']
else
Dir.home
end
end
end
end
102 changes: 102 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/profile/profile_file_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# frozen_string_literal: true

module Aws
# @api private
class ProfileFileParser
def initialize(profile_file_contents)
@profile_file_contents = profile_file_contents
@current_line_number = 0
end

def parse
initialize_profiles
@profiles
end

private

def initialize_profiles
@profiles = {}

@profile_file_contents.split(/[\r\n]+/).each do |line|
@current_line_number += 1
next if ProfileFileUtils.empty_line?(line) ||
ProfileFileUtils.comment_line?(line)

if ProfileFileUtils.profile_line?(line)
read_profile_line(line)
elsif ProfileFileUtils.property_continuation_line?(line)
read_property_continuation_line(line)
else
read_property_definition_line(line)
end
end
end

def read_profile_line(line)
line_without_comments = remove_trailing_comments(line, %w[# ;])
line_without_whitespace = ProfileFileUtils.trim_whitespace(line_without_comments)

unless line_without_whitespace[-1] == ']'
raise ArgumentError,
"Profile definition must end with ']' on line #{@current_line_number}"
end

line_without_brackets = line_without_whitespace[1..-2]

@current_profile = ProfileFileUtils.trim_whitespace(line_without_brackets)
@current_property = nil

@profiles[@current_profile] ||= {}
end

def read_property_definition_line(line)
unless @current_profile
raise ArgumentError,
"Expected a profile definition, found property on line #{@current_line_number}"
end

line_without_comments = remove_trailing_comments(line, [' #', ' ;', "\t#", "\t;"])
line_without_whitespace = ProfileFileUtils.trim_whitespace(line_without_comments)

key, value = ProfileFileUtils.parse_property_definition_line(
line_without_whitespace, "on line #{@current_line_number}"
)
@current_property = key
@profiles[@current_profile][key] = value
end

def read_property_continuation_line(line)
unless @current_profile
raise ArgumentError,
"Expected a profile definition, found continuation on line #{@current_line_number}"
end
unless @current_property
raise ArgumentError,
"Expected a property definition, found continuation on line #{@current_line_number}"
end

line = ProfileFileUtils.trim_whitespace(line)
profile_properties = @profiles[@current_profile]
current_property_value = profile_properties[@current_property]
new_property_value = "#{current_property_value}\n#{line}"

profile_properties[@current_property] = new_property_value
end

def remove_trailing_comments(line, *comment_patterns)
line[0...find_earliest_match(line, *comment_patterns)]
end

def find_earliest_match(line, search_patterns)
earliest_location = line.length

search_patterns.each do |pattern|
location = line.index(pattern)
earliest_location = [location, earliest_location].min if location
end

earliest_location
end
end
end
Loading
Loading