From 9ecfe5a04e2292a0d3d2a16c48f3c447e72b297c Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 10:35:28 -0500 Subject: [PATCH 1/7] Improve AWS config support for `source_profile` --- src/utilities/credentials.jl | 17 ++++++++-- test/AWSCredentials.jl | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/utilities/credentials.jl b/src/utilities/credentials.jl index 6e2bc5240..2d8885e47 100644 --- a/src/utilities/credentials.jl +++ b/src/utilities/credentials.jl @@ -36,11 +36,22 @@ function _get_ini_value( end function _aws_profile_config(ini::Inifile, profile::AbstractString) - if profile != "default" || !haskey(sections(ini), "default") - profile = "profile $profile" + # Prefer using "profile default" over "default" + section_name = if profile != "default" || haskey(sections(ini), "profile default") + "profile $profile" + else + "default" + end + + content = copy(get(sections(ini), section_name, IniFile.HTSS())) + source_profile = pop!(content, "source_profile", nothing) + + # Fallback on settings specified in the source profile + if !isnothing(source_profile) + content = merge(_aws_profile_config(ini, source_profile), content) end - return get(sections(ini), profile, Dict()) + return content end function _aws_profile_config(ini::Inifile, profile::Nothing) diff --git a/test/AWSCredentials.jl b/test/AWSCredentials.jl index 13f346883..ca719cfff 100644 --- a/test/AWSCredentials.jl +++ b/test/AWSCredentials.jl @@ -50,6 +50,70 @@ end end end +@testset "_aws_profile_config" begin + using AWS: _aws_profile_config + + @testset "source profile" begin + buffer = IOBuffer( + """ + [profile test] + output = json + region = us-east-1 + + [profile test:dev] + source_profile = test + role_arn = arn:aws:iam::123456789000:role/Dev + + [profile test:sub-dev] + source_profile = test:dev + role_arn = arn:aws:iam::123456789000:role/SubDev + """ + ) + ini = Inifile() + read(ini, buffer) + + # Basic profile + config = _aws_profile_config(ini, "test") + @test keys(config) ⊆ Set(["output", "region"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + + + config = _aws_profile_config(ini, "test:dev") + @test keys(config) ⊆ Set(["output", "region", "role_arn"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" + + # Conflicting keys should use the first defined entry + config = _aws_profile_config(ini, "test:sub-dev") + @test keys(config) ⊆ Set(["output", "region", "role_arn"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + @test config["role_arn"] == "arn:aws:iam::123456789000:role/SubDev" + end + + # AWS CLI (version v2.27.15) will use "profile default" over "default" when both are + # defined within the configuration. This is true when `AWS_PROFILE` is unset or + # `AWS_PROFILE="default". + @testset "default profile" begin + buffer = IOBuffer( + """ + [default] + region = default-1 + + [profile default] + region = default-2 + """ + ) + ini = Inifile() + read(ini, buffer) + + config = _aws_profile_config(ini, "default") + @test config["region"] == "default-2" + end +end + @testset "_aws_get_role" begin profile = "foobar" ini = Inifile() From a30b1b6ab845990caf270d0b08437ddabd565016 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 10:47:39 -0500 Subject: [PATCH 2/7] fixup --- test/AWSCredentials.jl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/AWSCredentials.jl b/test/AWSCredentials.jl index ca719cfff..db0f8405f 100644 --- a/test/AWSCredentials.jl +++ b/test/AWSCredentials.jl @@ -54,6 +54,7 @@ end using AWS: _aws_profile_config @testset "source profile" begin + #! format: off buffer = IOBuffer( """ [profile test] @@ -69,34 +70,46 @@ end role_arn = arn:aws:iam::123456789000:role/SubDev """ ) + #! format: on ini = Inifile() read(ini, buffer) - # Basic profile + # Only the fields from profile "test" config = _aws_profile_config(ini, "test") @test keys(config) ⊆ Set(["output", "region"]) @test config["output"] == "json" @test config["region"] == "us-east-1" - + # Combine the profile "test:dev" section with fields from profile "test" config = _aws_profile_config(ini, "test:dev") @test keys(config) ⊆ Set(["output", "region", "role_arn"]) @test config["output"] == "json" @test config["region"] == "us-east-1" @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" + # Ensure we haven't mutated the contents of the `ini` + section = sections(ini)["test:dev"] + @test !haskey(section, "region") + @test !haskey(section, "output") + # Conflicting keys should use the first defined entry config = _aws_profile_config(ini, "test:sub-dev") @test keys(config) ⊆ Set(["output", "region", "role_arn"]) @test config["output"] == "json" @test config["region"] == "us-east-1" @test config["role_arn"] == "arn:aws:iam::123456789000:role/SubDev" + + # Ensure we haven't mutated the contents of the `ini` + section = sections(ini)["test:sub-dev"] + @test !haskey(section, "region") + @test !haskey(section, "output") end # AWS CLI (version v2.27.15) will use "profile default" over "default" when both are # defined within the configuration. This is true when `AWS_PROFILE` is unset or # `AWS_PROFILE="default". @testset "default profile" begin + #! format: off buffer = IOBuffer( """ [default] @@ -106,6 +119,7 @@ end region = default-2 """ ) + #! format: on ini = Inifile() read(ini, buffer) From b21f5688a2cabc2a039e1c35dd0ae96317f65c3a Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 10:48:42 -0500 Subject: [PATCH 3/7] Make tests specificially unit tests --- test/AWSCredentials.jl | 78 ------------------------------------- test/runtests.jl | 1 + test/unit/AWSCredentials.jl | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 78 deletions(-) create mode 100644 test/unit/AWSCredentials.jl diff --git a/test/AWSCredentials.jl b/test/AWSCredentials.jl index db0f8405f..13f346883 100644 --- a/test/AWSCredentials.jl +++ b/test/AWSCredentials.jl @@ -50,84 +50,6 @@ end end end -@testset "_aws_profile_config" begin - using AWS: _aws_profile_config - - @testset "source profile" begin - #! format: off - buffer = IOBuffer( - """ - [profile test] - output = json - region = us-east-1 - - [profile test:dev] - source_profile = test - role_arn = arn:aws:iam::123456789000:role/Dev - - [profile test:sub-dev] - source_profile = test:dev - role_arn = arn:aws:iam::123456789000:role/SubDev - """ - ) - #! format: on - ini = Inifile() - read(ini, buffer) - - # Only the fields from profile "test" - config = _aws_profile_config(ini, "test") - @test keys(config) ⊆ Set(["output", "region"]) - @test config["output"] == "json" - @test config["region"] == "us-east-1" - - # Combine the profile "test:dev" section with fields from profile "test" - config = _aws_profile_config(ini, "test:dev") - @test keys(config) ⊆ Set(["output", "region", "role_arn"]) - @test config["output"] == "json" - @test config["region"] == "us-east-1" - @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" - - # Ensure we haven't mutated the contents of the `ini` - section = sections(ini)["test:dev"] - @test !haskey(section, "region") - @test !haskey(section, "output") - - # Conflicting keys should use the first defined entry - config = _aws_profile_config(ini, "test:sub-dev") - @test keys(config) ⊆ Set(["output", "region", "role_arn"]) - @test config["output"] == "json" - @test config["region"] == "us-east-1" - @test config["role_arn"] == "arn:aws:iam::123456789000:role/SubDev" - - # Ensure we haven't mutated the contents of the `ini` - section = sections(ini)["test:sub-dev"] - @test !haskey(section, "region") - @test !haskey(section, "output") - end - - # AWS CLI (version v2.27.15) will use "profile default" over "default" when both are - # defined within the configuration. This is true when `AWS_PROFILE` is unset or - # `AWS_PROFILE="default". - @testset "default profile" begin - #! format: off - buffer = IOBuffer( - """ - [default] - region = default-1 - - [profile default] - region = default-2 - """ - ) - #! format: on - ini = Inifile() - read(ini, buffer) - - config = _aws_profile_config(ini, "default") - @test config["region"] == "default-2" - end -end - @testset "_aws_get_role" begin profile = "foobar" ini = Inifile() diff --git a/test/runtests.jl b/test/runtests.jl index d59632c00..2b0d13ca8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,6 +66,7 @@ const AWS_CONFIG = Ref{AbstractAWSConfig}() @testset "Unit Tests" begin if RUN_UNIT_TESTS include("unit/AWS.jl") + include("unit/AWSCredentials.jl") else @warn "Skipping unit tests" end diff --git a/test/unit/AWSCredentials.jl b/test/unit/AWSCredentials.jl new file mode 100644 index 000000000..f212c12bb --- /dev/null +++ b/test/unit/AWSCredentials.jl @@ -0,0 +1,77 @@ +@testset "_aws_profile_config" begin + using AWS: _aws_profile_config + + @testset "source profile" begin + #! format: off + buffer = IOBuffer( + """ + [profile test] + output = json + region = us-east-1 + + [profile test:dev] + source_profile = test + role_arn = arn:aws:iam::123456789000:role/Dev + + [profile test:sub-dev] + source_profile = test:dev + role_arn = arn:aws:iam::123456789000:role/SubDev + """ + ) + #! format: on + ini = Inifile() + read(ini, buffer) + + # Only the fields from profile "test" + config = _aws_profile_config(ini, "test") + @test keys(config) ⊆ Set(["output", "region"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + + # Combine the profile "test:dev" section with fields from profile "test" + config = _aws_profile_config(ini, "test:dev") + @test keys(config) ⊆ Set(["output", "region", "role_arn"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" + + # Ensure we haven't mutated the contents of the `ini` + section = sections(ini)["test:dev"] + @test !haskey(section, "region") + @test !haskey(section, "output") + + # Conflicting keys should use the first defined entry + config = _aws_profile_config(ini, "test:sub-dev") + @test keys(config) ⊆ Set(["output", "region", "role_arn"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + @test config["role_arn"] == "arn:aws:iam::123456789000:role/SubDev" + + # Ensure we haven't mutated the contents of the `ini` + section = sections(ini)["test:sub-dev"] + @test !haskey(section, "region") + @test !haskey(section, "output") + end + + # AWS CLI (version v2.27.15) will use "profile default" over "default" when both are + # defined within the configuration. This is true when `AWS_PROFILE` is unset or + # `AWS_PROFILE="default". + @testset "default profile" begin + #! format: off + buffer = IOBuffer( + """ + [default] + region = default-1 + + [profile default] + region = default-2 + """ + ) + #! format: on + ini = Inifile() + read(ini, buffer) + + config = _aws_profile_config(ini, "default") + @test config["region"] == "default-2" + end +end From 06f947c8c0f910cb07652aa877928d09b00485fc Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 10:53:47 -0500 Subject: [PATCH 4/7] fixup --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 2b0d13ca8..bd2bee277 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,7 +23,7 @@ using Dates using Downloads using GitHub using HTTP -using IniFile: Inifile +using IniFile: Inifile, sections using JSON using OrderedCollections: LittleDict, OrderedDict using MbedTLS: digest, MD_SHA256, MD_MD5 From b8c523129682c609b936f7d7aa3ff2770c778e59 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 10:57:31 -0500 Subject: [PATCH 5/7] fixup --- test/unit/AWSCredentials.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/AWSCredentials.jl b/test/unit/AWSCredentials.jl index f212c12bb..73add8103 100644 --- a/test/unit/AWSCredentials.jl +++ b/test/unit/AWSCredentials.jl @@ -36,7 +36,7 @@ @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" # Ensure we haven't mutated the contents of the `ini` - section = sections(ini)["test:dev"] + section = sections(ini)["profile test:dev"] @test !haskey(section, "region") @test !haskey(section, "output") @@ -48,7 +48,7 @@ @test config["role_arn"] == "arn:aws:iam::123456789000:role/SubDev" # Ensure we haven't mutated the contents of the `ini` - section = sections(ini)["test:sub-dev"] + section = sections(ini)["profile test:sub-dev"] @test !haskey(section, "region") @test !haskey(section, "output") end From b2a626806f51284324897bab0d8e7e10d4b36f90 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 12:23:43 -0500 Subject: [PATCH 6/7] Make resursive profile merging optional --- src/AWSCredentials.jl | 2 +- src/utilities/credentials.jl | 25 +++++++++++++----------- test/unit/AWSCredentials.jl | 38 ++++++++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/AWSCredentials.jl b/src/AWSCredentials.jl index 9f83aa3ce..5c91634c7 100644 --- a/src/AWSCredentials.jl +++ b/src/AWSCredentials.jl @@ -657,7 +657,7 @@ function aws_get_region(; profile=nothing, config=nothing, default=DEFAULT_REGIO @something( get(ENV, "AWS_REGION", nothing), get(ENV, "AWS_DEFAULT_REGION", nothing), - get(_aws_profile_config(config, profile), "region", nothing), + get(_aws_profile_config(config, profile; recursive=true), "region", nothing), @mock(IMDS.region()), Some(default), ) diff --git a/src/utilities/credentials.jl b/src/utilities/credentials.jl index 2d8885e47..553694762 100644 --- a/src/utilities/credentials.jl +++ b/src/utilities/credentials.jl @@ -35,7 +35,7 @@ function _get_ini_value( return value end -function _aws_profile_config(ini::Inifile, profile::AbstractString) +function _aws_profile_config(ini::Inifile, profile::AbstractString; recursive::Bool=false) # Prefer using "profile default" over "default" section_name = if profile != "default" || haskey(sections(ini), "profile default") "profile $profile" @@ -44,27 +44,30 @@ function _aws_profile_config(ini::Inifile, profile::AbstractString) end content = copy(get(sections(ini), section_name, IniFile.HTSS())) - source_profile = pop!(content, "source_profile", nothing) - # Fallback on settings specified in the source profile - if !isnothing(source_profile) - content = merge(_aws_profile_config(ini, source_profile), content) + if recursive + source_profile = pop!(content, "source_profile", nothing) + + # Fallback on settings specified in the source profile + if !isnothing(source_profile) + content = merge(_aws_profile_config(ini, source_profile; recursive), content) + end end return content end -function _aws_profile_config(ini::Inifile, profile::Nothing) - return _aws_profile_config(ini, _aws_get_profile()) +function _aws_profile_config(ini::Inifile, profile::Nothing; kwargs...) + return _aws_profile_config(ini, _aws_get_profile(); kwargs...) end -function _aws_profile_config(config_file::AbstractString, profile) +function _aws_profile_config(config_file::AbstractString, profile; kwargs...) isfile(config_file) || return Dict() - return _aws_profile_config(read(Inifile(), config_file), profile) + return _aws_profile_config(read(Inifile(), config_file), profile; kwargs...) end -function _aws_profile_config(config_file::Nothing, profile) - return _aws_profile_config(dot_aws_config_file(), profile) +function _aws_profile_config(config_file::Nothing, profile; kwargs...) + return _aws_profile_config(dot_aws_config_file(), profile; kwargs...) end """ diff --git a/test/unit/AWSCredentials.jl b/test/unit/AWSCredentials.jl index 73add8103..a02623022 100644 --- a/test/unit/AWSCredentials.jl +++ b/test/unit/AWSCredentials.jl @@ -1,7 +1,37 @@ @testset "_aws_profile_config" begin using AWS: _aws_profile_config - @testset "source profile" begin + @testset "non-recursive" begin + #! format: off + buffer = IOBuffer( + """ + [profile test] + output = json + region = us-east-1 + + [profile test:dev] + source_profile = test + role_arn = arn:aws:iam::123456789000:role/Dev + """ + ) + #! format: on + ini = Inifile() + read(ini, buffer) + + # Only the fields from profile "test" + config = _aws_profile_config(ini, "test") + @test keys(config) ⊆ Set(["output", "region"]) + @test config["output"] == "json" + @test config["region"] == "us-east-1" + + # Only the fields from profile "test:dev" + config = _aws_profile_config(ini, "test:dev") + @test keys(config) ⊆ Set(["source_profile", "role_arn"]) + @test config["source_profile"] == "test" + @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" + end + + @testset "recursive" begin #! format: off buffer = IOBuffer( """ @@ -23,13 +53,13 @@ read(ini, buffer) # Only the fields from profile "test" - config = _aws_profile_config(ini, "test") + config = _aws_profile_config(ini, "test"; recursive=true) @test keys(config) ⊆ Set(["output", "region"]) @test config["output"] == "json" @test config["region"] == "us-east-1" # Combine the profile "test:dev" section with fields from profile "test" - config = _aws_profile_config(ini, "test:dev") + config = _aws_profile_config(ini, "test:dev"; recursive=true) @test keys(config) ⊆ Set(["output", "region", "role_arn"]) @test config["output"] == "json" @test config["region"] == "us-east-1" @@ -41,7 +71,7 @@ @test !haskey(section, "output") # Conflicting keys should use the first defined entry - config = _aws_profile_config(ini, "test:sub-dev") + config = _aws_profile_config(ini, "test:sub-dev"; recursive=true) @test keys(config) ⊆ Set(["output", "region", "role_arn"]) @test config["output"] == "json" @test config["region"] == "us-east-1" From 464909b2b66bbf76f1da5409b2c11da4008d8dba Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 2 Jul 2025 15:54:58 -0500 Subject: [PATCH 7/7] Inheriting is not supported --- src/AWSCredentials.jl | 2 +- src/utilities/credentials.jl | 27 ++++-------- test/AWSCredentials.jl | 4 +- test/unit/AWSCredentials.jl | 84 +++++++++--------------------------- 4 files changed, 30 insertions(+), 87 deletions(-) diff --git a/src/AWSCredentials.jl b/src/AWSCredentials.jl index 5c91634c7..9f83aa3ce 100644 --- a/src/AWSCredentials.jl +++ b/src/AWSCredentials.jl @@ -657,7 +657,7 @@ function aws_get_region(; profile=nothing, config=nothing, default=DEFAULT_REGIO @something( get(ENV, "AWS_REGION", nothing), get(ENV, "AWS_DEFAULT_REGION", nothing), - get(_aws_profile_config(config, profile; recursive=true), "region", nothing), + get(_aws_profile_config(config, profile), "region", nothing), @mock(IMDS.region()), Some(default), ) diff --git a/src/utilities/credentials.jl b/src/utilities/credentials.jl index 553694762..20e87d0cb 100644 --- a/src/utilities/credentials.jl +++ b/src/utilities/credentials.jl @@ -35,7 +35,7 @@ function _get_ini_value( return value end -function _aws_profile_config(ini::Inifile, profile::AbstractString; recursive::Bool=false) +function _aws_profile_config(ini::Inifile, profile::AbstractString) # Prefer using "profile default" over "default" section_name = if profile != "default" || haskey(sections(ini), "profile default") "profile $profile" @@ -43,31 +43,20 @@ function _aws_profile_config(ini::Inifile, profile::AbstractString; recursive::B "default" end - content = copy(get(sections(ini), section_name, IniFile.HTSS())) - - if recursive - source_profile = pop!(content, "source_profile", nothing) - - # Fallback on settings specified in the source profile - if !isnothing(source_profile) - content = merge(_aws_profile_config(ini, source_profile; recursive), content) - end - end - - return content + return copy(get(sections(ini), section_name, IniFile.HTSS())) end -function _aws_profile_config(ini::Inifile, profile::Nothing; kwargs...) - return _aws_profile_config(ini, _aws_get_profile(); kwargs...) +function _aws_profile_config(ini::Inifile, profile::Nothing) + return _aws_profile_config(ini, _aws_get_profile()) end -function _aws_profile_config(config_file::AbstractString, profile; kwargs...) +function _aws_profile_config(config_file::AbstractString, profile) isfile(config_file) || return Dict() - return _aws_profile_config(read(Inifile(), config_file), profile; kwargs...) + return _aws_profile_config(read(Inifile(), config_file), profile) end -function _aws_profile_config(config_file::Nothing, profile; kwargs...) - return _aws_profile_config(dot_aws_config_file(), profile; kwargs...) +function _aws_profile_config(config_file::Nothing, profile) + return _aws_profile_config(dot_aws_config_file(), profile) end """ diff --git a/test/AWSCredentials.jl b/test/AWSCredentials.jl index 13f346883..af2f13c0d 100644 --- a/test/AWSCredentials.jl +++ b/test/AWSCredentials.jl @@ -213,7 +213,6 @@ end """ [profile test] output = json - region = us-east-1 [profile test:dev] source_profile = test @@ -226,8 +225,6 @@ end [profile test2] aws_access_key_id = WRONG_ACCESS_ID aws_secret_access_key = WRONG_ACCESS_KEY - output = json - region = us-east-1 [profile test3] source_profile = test:dev @@ -264,6 +261,7 @@ end "AWS_DEFAULT_PROFILE" => "test", "AWS_PROFILE" => nothing, "AWS_ACCESS_KEY_ID" => nothing, + "AWS_REGION" => "us-east-1", ) do @testset "Loading" begin # Check credentials load diff --git a/test/unit/AWSCredentials.jl b/test/unit/AWSCredentials.jl index a02623022..2baa12bf7 100644 --- a/test/unit/AWSCredentials.jl +++ b/test/unit/AWSCredentials.jl @@ -1,86 +1,42 @@ @testset "_aws_profile_config" begin using AWS: _aws_profile_config - @testset "non-recursive" begin + @testset "access" begin #! format: off buffer = IOBuffer( """ - [profile test] + [profile A] output = json region = us-east-1 - [profile test:dev] - source_profile = test - role_arn = arn:aws:iam::123456789000:role/Dev + [profile B] + role_arn = arn:aws:iam::123456789000:role/A + source_profile = A """ ) #! format: on ini = Inifile() read(ini, buffer) - # Only the fields from profile "test" - config = _aws_profile_config(ini, "test") + # Retrieve fields from profile "A" + config = _aws_profile_config(ini, "A") @test keys(config) ⊆ Set(["output", "region"]) @test config["output"] == "json" @test config["region"] == "us-east-1" - # Only the fields from profile "test:dev" - config = _aws_profile_config(ini, "test:dev") - @test keys(config) ⊆ Set(["source_profile", "role_arn"]) - @test config["source_profile"] == "test" - @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" - end - - @testset "recursive" begin - #! format: off - buffer = IOBuffer( - """ - [profile test] - output = json - region = us-east-1 - - [profile test:dev] - source_profile = test - role_arn = arn:aws:iam::123456789000:role/Dev - - [profile test:sub-dev] - source_profile = test:dev - role_arn = arn:aws:iam::123456789000:role/SubDev - """ - ) - #! format: on - ini = Inifile() - read(ini, buffer) - - # Only the fields from profile "test" - config = _aws_profile_config(ini, "test"; recursive=true) - @test keys(config) ⊆ Set(["output", "region"]) - @test config["output"] == "json" - @test config["region"] == "us-east-1" - - # Combine the profile "test:dev" section with fields from profile "test" - config = _aws_profile_config(ini, "test:dev"; recursive=true) - @test keys(config) ⊆ Set(["output", "region", "role_arn"]) - @test config["output"] == "json" - @test config["region"] == "us-east-1" - @test config["role_arn"] == "arn:aws:iam::123456789000:role/Dev" - - # Ensure we haven't mutated the contents of the `ini` - section = sections(ini)["profile test:dev"] - @test !haskey(section, "region") - @test !haskey(section, "output") - - # Conflicting keys should use the first defined entry - config = _aws_profile_config(ini, "test:sub-dev"; recursive=true) - @test keys(config) ⊆ Set(["output", "region", "role_arn"]) - @test config["output"] == "json" - @test config["region"] == "us-east-1" - @test config["role_arn"] == "arn:aws:iam::123456789000:role/SubDev" - - # Ensure we haven't mutated the contents of the `ini` - section = sections(ini)["profile test:sub-dev"] - @test !haskey(section, "region") - @test !haskey(section, "output") + # Ensure that mutating the returned dictionary does not mutated the contents of the + # `ini` + config["output"] = "yaml-stream" + section = sections(ini)["profile A"] + @test section["output"] == "json" + + # Use of `source_profile` does not inherit properties from the source. This mirrors + # the AWS CLI (version v2.27.15) behavior for `region` which can be seen using + # `aws configure list --profile X` + config = _aws_profile_config(ini, "B") + @test keys(config) ⊆ Set(["role_arn", "source_profile"]) + @test config["role_arn"] == "arn:aws:iam::123456789000:role/A" + @test config["source_profile"] == "A" end # AWS CLI (version v2.27.15) will use "profile default" over "default" when both are