├── .rspec ├── lib ├── aws_config │ ├── version.rb │ ├── profile_resolver.rb │ ├── store.rb │ ├── profile.rb │ └── parser.rb └── aws_config.rb ├── Gemfile ├── Guardfile ├── CHANGELOD.md ├── Rakefile ├── spec ├── samples │ ├── credentials.txt │ └── config.txt ├── spec_helper.rb └── lib │ ├── aws_config_spec.rb │ └── aws_config │ ├── profile_resolver_spec.rb │ ├── profile_spec.rb │ └── parser_spec.rb ├── .gitignore ├── .github └── workflows │ └── test.yml ├── aws_config.gemspec ├── LICENSE.txt └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /lib/aws_config/version.rb: -------------------------------------------------------------------------------- 1 | module AWSConfig 2 | VERSION = "0.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development, :test do 6 | gem "guard-rspec" 7 | end 8 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec do 2 | watch(%r{^spec/.+_spec\.rb$}) 3 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } 4 | end 5 | 6 | -------------------------------------------------------------------------------- /lib/aws_config.rb: -------------------------------------------------------------------------------- 1 | require "aws_config/version" 2 | require "aws_config/parser" 3 | require "aws_config/store" 4 | 5 | module AWSConfig 6 | extend Store 7 | end 8 | -------------------------------------------------------------------------------- /CHANGELOD.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Unreleased 4 | 5 | ## 0.1.1 6 | 7 | ### Bug Fixes 8 | 9 | * File.exists? was removed, use File.exist? instead #11 [@thermistor] 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | require 'rspec/core' 4 | require 'rspec/core/rake_task' 5 | 6 | task :default => :spec 7 | 8 | desc "Run all specs in spec directory" 9 | RSpec::Core::RakeTask.new(:spec) 10 | -------------------------------------------------------------------------------- /spec/samples/credentials.txt: -------------------------------------------------------------------------------- 1 | [default] 2 | aws_access_key_id=DefaultAccessKey01 3 | aws_secret_access_key=Default/Secret/Access/Key/02 4 | 5 | [testing] 6 | aws_access_key_id=TestingAccessKey03 7 | aws_secret_access_key=Testing/Secret/Access/Key/04 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | vendor 19 | -------------------------------------------------------------------------------- /spec/samples/config.txt: -------------------------------------------------------------------------------- 1 | [default] 2 | # Optional, to define default region for this profile. 3 | region=us-west-1 4 | source_profile=default 5 | # Nested 6 | s3= 7 | max_concurrent_requests=100101 8 | max_queue_size=20 9 | output=json 10 | 11 | [profile testing] 12 | region=us-west-2 13 | source_profile=testing 14 | 15 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) 2 | require "aws_config" 3 | 4 | RSpec.configure do |config| 5 | config.run_all_when_everything_filtered = true 6 | config.filter_run :focus 7 | 8 | # Run specs in random order to surface order dependencies. If you find an 9 | # order dependency and want to debug it, you can fix the order by providing 10 | # the seed, which is printed after each run. 11 | # --seed 1234 12 | config.order = 'random' 13 | end 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | ruby-version: 16 | - 2.6.10 17 | - 2.7.8 18 | - 3.0.6 19 | - 3.1.4 20 | - 3.2.2 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: ruby/setup-ruby@v1 24 | with: 25 | ruby-version: "${{ matrix.ruby-version }}" 26 | bundler-cache: true 27 | - run: "bundle exec rake spec" 28 | -------------------------------------------------------------------------------- /aws_config.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'aws_config/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "aws_config" 8 | spec.version = AWSConfig::VERSION 9 | spec.authors = ["Masato Ikeda"] 10 | spec.email = ["masato.ikeda@gmail.com"] 11 | spec.description = %q{AWSConfig is a parser for AWS_CONFIG_FILE used in aws-cli.} 12 | spec.summary = %q{AWSConfig is a parser for AWS_CONFIG_FILE used in aws-cli.} 13 | spec.homepage = "https://github.com/a2ikm/aws_config" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", ">= 1.3" 22 | spec.add_development_dependency "rake" 23 | spec.add_development_dependency "rspec" 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Masato Ikeda 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/aws_config/profile_resolver.rb: -------------------------------------------------------------------------------- 1 | require "aws_config/profile" 2 | 3 | module AWSConfig 4 | class ProfileResolver 5 | attr_reader :profiles, :wanted_profiles 6 | 7 | def initialize 8 | @profiles = {} 9 | @wanted_profiles = {} 10 | end 11 | 12 | def add(profs) 13 | profs.each do |name, profile| 14 | if profiles.key? name 15 | profiles[name].merge! profile 16 | else 17 | profiles[name] = profile 18 | end 19 | resolve_source_profile(name, profile) if profile.has_key? "source_profile" 20 | provides_source_profile(name, profile) 21 | end 22 | end 23 | 24 | private 25 | 26 | def resolve_source_profile(name, profile) 27 | source_profile = profile.source_profile 28 | if profiles.key? source_profile 29 | profile["source_profile"] = profiles[source_profile] 30 | else 31 | (wanted_profiles[source_profile] ||= []) << name 32 | end 33 | end 34 | 35 | def provides_source_profile(name, profile) 36 | return unless wanted_profiles.key? name 37 | wanted_profiles[name].each do |wanted_by| 38 | profiles[wanted_by]["source_profile"] = profile 39 | end 40 | wanted_profiles.delete name 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/lib/aws_config_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe AWSConfig do 4 | let(:sample_config_file) { File.expand_path("../../samples/config.txt", __FILE__) } 5 | let(:sample_creds_file) { File.expand_path("../../samples/credentials.txt", __FILE__) } 6 | before do 7 | AWSConfig.config_file = sample_config_file 8 | AWSConfig.credentials_file = sample_creds_file 9 | end 10 | 11 | it "should return an entry in a profile via method" do 12 | expect(described_class.default.aws_access_key_id).to eq "DefaultAccessKey01" 13 | expect(described_class.default.region).to eq "us-west-1" 14 | expect(described_class.default.output).to eq "json" 15 | expect(described_class.default.s3.max_concurrent_requests).to eq "100101" 16 | expect(described_class.default.s3.max_queue_size).to eq "20" 17 | end 18 | 19 | it "should return an entry in a profile like hashes" do 20 | expect(described_class["default"]["aws_access_key_id"]).to eq "DefaultAccessKey01" 21 | expect(described_class["default"]["region"]).to eq "us-west-1" 22 | expect(described_class["default"]["output"]).to eq "json" 23 | expect(described_class["default"]["s3"]["max_concurrent_requests"]). 24 | to eq "100101" 25 | expect(described_class["default"]["s3"]["max_queue_size"]).to eq "20" 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/aws_config/store.rb: -------------------------------------------------------------------------------- 1 | require "aws_config/profile_resolver" 2 | module AWSConfig 3 | module Store 4 | def profiles 5 | @profiles ||= begin 6 | if File.exist?(config_file) 7 | profile_resolver = ProfileResolver.new 8 | profile_resolver.add Parser.parse(File.read(credentials_file), true) 9 | profile_resolver.add Parser.parse(File.read(config_file)) 10 | profile_resolver.profiles 11 | else 12 | Hash.new 13 | end 14 | end 15 | end 16 | 17 | def config_file 18 | @config_file || ENV["AWS_CONFIG_FILE"] || File.join(ENV["HOME"], ".aws/config") 19 | end 20 | 21 | def config_file=(path) 22 | @config_file = path 23 | @profiles = nil 24 | end 25 | 26 | def credentials_file 27 | @credentials_file || ENV["AWS_SHARED_CREDENTIALS_FILE"] || File.join(ENV["HOME"], ".aws/credentials") 28 | end 29 | 30 | def credentials_file=(path) 31 | @credentials_file = path 32 | @profiles = nil 33 | end 34 | 35 | def [](profile) 36 | profiles[profile.to_s] 37 | end 38 | 39 | def has_profile?(profile) 40 | profiles.has_key?(profile.to_s) 41 | end 42 | 43 | def respond_to?(id, include_all = false) 44 | has_profile?(id) || super 45 | end 46 | 47 | def method_missing(id, *args) 48 | if has_profile?(id) 49 | self[id] 50 | else 51 | super 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/aws_config/profile.rb: -------------------------------------------------------------------------------- 1 | module AWSConfig 2 | class Profile 3 | attr_reader :name, :entries 4 | 5 | def initialize(name, entries) 6 | @name = name.to_s 7 | @entries = entries 8 | end 9 | 10 | def [](key) 11 | key = key.to_s 12 | if entries.has_key?(key) 13 | entries[key] 14 | elsif source_profile? 15 | entries["source_profile"][key] 16 | else 17 | nil 18 | end 19 | end 20 | 21 | def []=(key, value) 22 | key = key.to_s 23 | entries[key] = value 24 | end 25 | 26 | def has_key?(key) 27 | key = key.to_s 28 | entries.has_key?(key) 29 | end 30 | 31 | def source_profile? 32 | entries.key?("source_profile") && entries["source_profile"].is_a?(Profile) 33 | end 34 | 35 | def respond_to?(id, include_all = false) 36 | has_key?(id) || super 37 | end 38 | 39 | def method_missing(id, *args) 40 | if has_key?(id) 41 | self[id] 42 | elsif source_profile? 43 | if entries["source_profile"].respond_to?(id) 44 | entries["source_profile"].send(id, args) 45 | else 46 | super 47 | end 48 | else 49 | super 50 | end 51 | end 52 | 53 | def config_hash 54 | { 55 | access_key_id: self["aws_access_key_id"], 56 | secret_access_key: self["aws_secret_access_key"], 57 | region: self["region"] 58 | } 59 | end 60 | 61 | def merge!(other) 62 | @entries = Parser.from_hash(entries.merge(other.entries)) 63 | self 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/aws_config/parser.rb: -------------------------------------------------------------------------------- 1 | require "strscan" 2 | require "aws_config/profile" 3 | 4 | module AWSConfig 5 | class Parser 6 | attr_accessor :credential_file_mode 7 | 8 | def self.parse(string, credential_file_mode = false) 9 | from_hash(new.tokenize(string)) 10 | end 11 | 12 | def self.from_hash(hash) 13 | hash.inject({}) do |memo, (k,v)| 14 | if v.is_a?(Hash) 15 | memo[k]=Profile.new(k,v) 16 | else 17 | memo[k] = v 18 | end 19 | 20 | memo 21 | end 22 | end 23 | 24 | def tokenize(string) 25 | tokens = { } 26 | current_profile = nil 27 | current_nesting = nil 28 | 29 | string.lines.each do |line| 30 | comment = line.match(/^\s*#.*/) 31 | blank = line.match(/^\s*$/) 32 | next if comment || blank 33 | 34 | profile_match = line.match(/\[\s*(profile)?\s*(?[^\]]+)\s*\]/) 35 | if profile_match 36 | current_profile = profile_match[:profile] 37 | tokens[current_profile] ||= {} 38 | next 39 | end 40 | 41 | nest_key_value = line.match(/(?^\s+)?(?[^\s=#]+)\s*=\s*(?[^\s#]+)/) 42 | if nest_key_value 43 | nest, key, value = !!nest_key_value[:nest], nest_key_value[:key], nest_key_value[:value] 44 | if nest 45 | fail("Nesting without a parent error") if current_nesting.nil? 46 | tokens[current_profile][current_nesting][key] = value 47 | else 48 | current_nesting = nil 49 | tokens[current_profile][key] = value 50 | end 51 | next 52 | end 53 | 54 | nesting = line.match(/(?[^\s=#]+)\s*=.*/) 55 | if nesting 56 | current_nesting = nesting[:name] 57 | tokens[current_profile][current_nesting] ||= {} 58 | end 59 | end 60 | tokens 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/a2ikm/aws_config/actions/workflows/test.yml/badge.svg)](https://github.com/a2ikm/aws_config/actions) 2 | 3 | # AWSConfig 4 | 5 | AWSConfig is a parser for AWS_CONFIG_FILE used in [aws-cli](https://github.com/aws/aws-cli). 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | gem 'aws_config' 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install aws_config 20 | 21 | ## Usage 22 | 23 | It parses `~/.aws/config` and `~/.aws/credentials` by default. 24 | 25 | If your aws config files look like this: 26 | 27 | **Credentials file** 28 | ``` 29 | [default] 30 | aws_access_key_id=DefaultAccessKey01 31 | aws_secret_access_key=Default/Secret/Access/Key/02 32 | 33 | [testing] 34 | aws_access_key_id=TestingAccessKey03 35 | aws_secret_access_key=Testing/Secret/Access/Key/04 36 | ``` 37 | 38 | **Config file** 39 | ``` 40 | [default] 41 | # Optional, to define default region for this profile. 42 | region=us-west-1 43 | source_profile=default 44 | 45 | [profile testing] 46 | role_arn=arn:example:283671836 47 | region=us-west-2 48 | source_profile=testing 49 | 50 | [profile with_mfa] 51 | source_profile=testing 52 | region=ap-southeast-2 53 | mfa_serial=arn:mfa_device:151235152134 54 | ``` 55 | 56 | you can access it like: 57 | ```ruby 58 | require "aws_config" 59 | 60 | puts AWSConfig.default.aws_access_key_id #=> DefaultAccessKey01 61 | puts AWSConfig.default.region #=> Default/Secret/Access/Key/02 62 | ``` 63 | 64 | also you can do like hashes: 65 | ```ruby 66 | puts AWSConfig["default"]["aws_access_key_id"] #=> DefaultAccessKey01 67 | puts AWSConfig["default"]["region"] #=> Default/Secret/Access/Key/02 68 | ``` 69 | 70 | If your config contains chained profiles using the `source_profile` property, 71 | you can still access the source profiles properties from the top i.e 72 | ```ruby 73 | require "aws_config" 74 | 75 | puts AWSConfig.with_mfa.role_arn #=> arn:example:283671836 76 | puts AWSConfig.with_mfa.region #=> ap-southeast-2 77 | ``` 78 | 79 | If you want to use with aws-sdk-ruby, you can configure like: 80 | ```ruby 81 | AWS.config(AWSConfig.default.config_hash) 82 | ``` 83 | 84 | ## Contributing 85 | 86 | 1. Fork it 87 | 2. Create your feature branch (`git checkout -b my-new-feature`) 88 | 3. Commit your changes (`git commit -am 'Add some feature'`) 89 | 4. Push to the branch (`git push origin my-new-feature`) 90 | 5. Create new Pull Request 91 | -------------------------------------------------------------------------------- /spec/lib/aws_config/profile_resolver_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe AWSConfig::ProfileResolver do 4 | subject { described_class.new } 5 | 6 | let(:default_profile) do 7 | AWSConfig::Profile.new( 8 | "default", 9 | "aws_access_key_id" => "DefaultAccessId01", 10 | "aws_secret_access_key" => "DefaultSecretKey01", 11 | "region" => "us-west-1" 12 | ) 13 | end 14 | let(:testing_profile) do 15 | AWSConfig::Profile.new( 16 | "testing", 17 | "aws_access_key_id" => "TestingAccessId01", 18 | "aws_secret_access_key" => "TestingSecretKey01", 19 | "region" => "us-west-1" 20 | ) 21 | end 22 | let(:profile_with_source) do 23 | AWSConfig::Profile.new( 24 | "with_source", 25 | "region" => "ap-southeast-2", 26 | "source_profile" => "testing" 27 | ) 28 | end 29 | describe "#add" do 30 | context "when all profiles resolve" do 31 | let(:profiles) do 32 | { 33 | default_profile.name => default_profile, 34 | testing_profile.name => testing_profile, 35 | profile_with_source.name => profile_with_source 36 | } 37 | end 38 | 39 | it "resolves the sources" do 40 | subject.add profiles 41 | expect(profiles["with_source"].source_profile).to be testing_profile 42 | end 43 | end 44 | 45 | context "when some profles do not resolve" do 46 | let(:unresolving) do 47 | AWSConfig::Profile.new( 48 | "unresolving", 49 | "aws_access_key_id" => "UnresolvingAccessId01", 50 | "aws_secret_access_key" => "UnresolvingSecretKey01", 51 | "region" => "us-west-1", 52 | "source_profile" => "doesnt_exist" 53 | ) 54 | end 55 | 56 | let(:missing_profile) do 57 | AWSConfig::Profile.new( 58 | "doesnt_exist", 59 | "aws_session_token" => "uk-west-2" 60 | ) 61 | end 62 | let(:profiles) do 63 | { 64 | default_profile.name => default_profile, 65 | testing_profile.name => testing_profile, 66 | profile_with_source.name => profile_with_source, 67 | unresolving.name => unresolving 68 | } 69 | end 70 | 71 | it "resolves the sources it can" do 72 | subject.add profiles 73 | expect(profiles["with_source"].source_profile).to be testing_profile 74 | end 75 | 76 | it "should remember the unresolved source profile" do 77 | subject.add profiles 78 | expect(subject.wanted_profiles).to eq("doesnt_exist" => ["unresolving"]) 79 | end 80 | 81 | it "should resolve the missing profile if it is added later" do 82 | subject.add profiles 83 | expect(subject.wanted_profiles).to eq("doesnt_exist" => ["unresolving"]) 84 | subject.add missing_profile.name => missing_profile 85 | expect(subject.wanted_profiles).to eq({}) 86 | expect(subject.profiles["unresolving"].source_profile).to be missing_profile 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /spec/lib/aws_config/profile_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe AWSConfig::Profile do 4 | context "regardless of source_profile" do 5 | subject { 6 | AWSConfig::Profile.new( 7 | "default", 8 | "aws_access_key_id" => "DefaultAccessKey01", 9 | "aws_secret_access_key" => "Default/Secret/Access/Key/02", 10 | "region" => "us-west-1" 11 | ) 12 | } 13 | 14 | it "should return given name via method" do 15 | expect(subject.name).to eq "default" 16 | end 17 | 18 | it "should respond to methods whose names are same as given entries" do 19 | expect(subject).to respond_to :aws_access_key_id 20 | expect(subject).to respond_to :aws_secret_access_key 21 | expect(subject).to respond_to :region 22 | end 23 | 24 | it "should return given entries via methods" do 25 | expect(subject.aws_access_key_id).to eq "DefaultAccessKey01" 26 | expect(subject.aws_secret_access_key).to eq "Default/Secret/Access/Key/02" 27 | expect(subject.region).to eq "us-west-1" 28 | end 29 | 30 | it "should return given entries via hash-like methods" do 31 | expect(subject[:aws_access_key_id]).to eq "DefaultAccessKey01" 32 | expect(subject[:aws_secret_access_key]).to eq "Default/Secret/Access/Key/02" 33 | expect(subject[:region]).to eq "us-west-1" 34 | end 35 | 36 | it "should raise exceptions if unknown entry is called via methods" do 37 | expect { subject.unknown_method }.to raise_error NoMethodError 38 | end 39 | 40 | it "should return a hash for aws-sdk-ruby's configuration format" do 41 | expect(subject.config_hash).to eq({ 42 | access_key_id: "DefaultAccessKey01", 43 | secret_access_key: "Default/Secret/Access/Key/02", 44 | region: "us-west-1" 45 | }) 46 | end 47 | end 48 | 49 | context "with source_profile" do 50 | let(:source_profile) do 51 | AWSConfig::Profile.new( 52 | "default", 53 | "region" => "ap-southeast-2", 54 | "aws_access_key_id" => "DefaultAccessKey01", 55 | "aws_secret_access_key" => "Default/Secret/Access/Key/02" 56 | ) 57 | end 58 | subject do 59 | AWSConfig::Profile.new( 60 | "testing", 61 | "region" => "us-west-1", 62 | "source_profile" => source_profile 63 | ) 64 | end 65 | context "when called like a method" do 66 | let(:region) { "us-west-1" } 67 | let(:access_key_id) { "DefaultAccesskey01" } 68 | it "should return values directly on the profile" do 69 | expect(subject.region).to eq region 70 | end 71 | 72 | it "should check the source profile if the desired key is missing" do 73 | expect(source_profile).to receive(:aws_access_key_id).and_return(access_key_id) 74 | expect(subject.aws_access_key_id).to eq access_key_id 75 | end 76 | end 77 | 78 | context "when called like a hash" do 79 | let(:region) { "us-west-1" } 80 | let(:access_key_id) { "DefaultAccesskey01" } 81 | it "should return values directly on the profile" do 82 | expect(subject["region"]).to eq region 83 | end 84 | 85 | it "should check the source profile if the desired key is missing" do 86 | expect(source_profile).to receive(:[]).with("aws_access_key_id").and_return(access_key_id) 87 | expect(subject["aws_access_key_id"]).to eq access_key_id 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /spec/lib/aws_config/parser_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe AWSConfig::Parser do 4 | describe "#tokenize" do 5 | subject { described_class.new.send(:tokenize, string) } 6 | 7 | context "only default profile" do 8 | let(:string) { <<-EOC } 9 | [default] 10 | aws_access_key_id=DefaultAccessKey01 11 | aws_secret_access_key=Default/Secret/Access/Key/02 12 | region=us-west-1 13 | EOC 14 | it { should eq( 15 | "default" => { 16 | "aws_access_key_id" => "DefaultAccessKey01", 17 | "aws_secret_access_key" => "Default/Secret/Access/Key/02", 18 | "region" => "us-west-1" } 19 | ) } 20 | end 21 | 22 | context "with nesting" do 23 | let(:string) { <<-EOC } 24 | [default] 25 | region=us-east-1 26 | s3= 27 | max_concurrent_requests=100101 28 | max_queue_size=20 29 | output=json 30 | EOC 31 | it { should eq( 32 | "default" => { 33 | "region" => "us-east-1", 34 | "s3" => { 35 | "max_concurrent_requests" => "100101", 36 | "max_queue_size" => "20" 37 | }, 38 | "output" => "json" } 39 | ) } 40 | end 41 | 42 | context "invalid nesting" do 43 | let(:string) { <<-EOC } 44 | [default] 45 | region=us-east-1 46 | s3= 47 | max_concurrent_requests=100101 48 | output=json 49 | max_queue_size=20 50 | EOC 51 | it "raises an exception" do 52 | expect { subject }.to raise_error(RuntimeError, "Nesting without a parent error") 53 | end 54 | end 55 | 56 | context "multiple nesting" do 57 | let(:string) { <<-EOC } 58 | [default] 59 | region=us-east-1 60 | s3= 61 | max_concurrent_requests=100101 62 | max_queue_size=20 63 | output=json 64 | api_versions = 65 | ec2 = 2015-03-01 66 | cloudfront = 2015-09-17 67 | EOC 68 | it { should eq( 69 | "default" => { 70 | "region" => "us-east-1", 71 | "s3" => { 72 | "max_concurrent_requests" => "100101", 73 | "max_queue_size" => "20" 74 | }, 75 | "output" => "json", 76 | "api_versions" => { 77 | "ec2" => "2015-03-01", 78 | "cloudfront" => "2015-09-17" 79 | } 80 | } 81 | ) } 82 | end 83 | 84 | context "with comments" do 85 | let(:string) { <<-EOC } 86 | [default] # inline comment in a profile 87 | region=us-east-1 # inline comment in top level element 88 | s3= # inline comment in nesting openning 89 | max_concurrent_requests=100101 # inline comment in a nested element 90 | # comment prefixed with spaces 91 | # comment in a new line 92 | EOC 93 | it { should eq( 94 | "default" => { 95 | "region" => "us-east-1", 96 | "s3" => { 97 | "max_concurrent_requests" => "100101", 98 | } 99 | } 100 | ) } 101 | end 102 | 103 | context "default and named profiles" do 104 | let(:string) { <<-EOC } 105 | [default] 106 | aws_access_key_id=DefaultAccessKey01 107 | 108 | [profile testing] 109 | aws_access_key_id=TestingAccessKey03 110 | EOC 111 | it { should eq( 112 | "default" => { "aws_access_key_id" => "DefaultAccessKey01" }, 113 | "testing" => { "aws_access_key_id" => "TestingAccessKey03" } 114 | ) } 115 | 116 | context "Comment line" do 117 | let(:string) { <<-EOC } 118 | [default] 119 | # THIS IS COMMENT # 120 | aws_access_key_id=DefaultAccessKey01 121 | EOC 122 | it { should eq( 123 | "default" => { "aws_access_key_id" => "DefaultAccessKey01" } 124 | ) } 125 | end 126 | 127 | context "Blank line" do 128 | let(:string) { <<-EOC } 129 | [default] 130 | 131 | 132 | aws_access_key_id=DefaultAccessKey01 133 | EOC 134 | it { should eq( 135 | "default" => { "aws_access_key_id" => "DefaultAccessKey01" } 136 | ) } 137 | end 138 | end 139 | 140 | context "in credential file mode" do 141 | subject do 142 | sut = described_class.new 143 | sut.credential_file_mode = true 144 | sut.send(:tokenize, string) 145 | end 146 | context "with only the default profile" do 147 | let(:string) { <<-EOC } 148 | [default] 149 | aws_access_key_id=DefaultAccessKey01 150 | aws_secret_access_key=Default/Secret/Access/Key/02 151 | EOC 152 | it { should eq( 153 | "default" => { 154 | "aws_access_key_id" => "DefaultAccessKey01", 155 | "aws_secret_access_key" => "Default/Secret/Access/Key/02" 156 | } 157 | ) } 158 | end 159 | 160 | context "with the default and named profiles" do 161 | let(:string) { <<-EOC } 162 | [default] 163 | aws_access_key_id=DefaultAccessKey01 164 | 165 | [testing] 166 | aws_access_key_id=TestingAccessKey03 167 | EOC 168 | it { should eq( 169 | "default" => { "aws_access_key_id" => "DefaultAccessKey01" }, 170 | "testing" => { "aws_access_key_id" => "TestingAccessKey03" } 171 | ) } 172 | end 173 | end 174 | end 175 | end 176 | --------------------------------------------------------------------------------