Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardd committed Oct 9, 2024
1 parent 3f60706 commit 4a0f3ab
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 32 deletions.
1 change: 1 addition & 0 deletions lib/ex_aws/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule ExAws.Config do
# TODO: Add proper documentation?

@common_config [
:auth_cache_refresh_lead_time,
:http_client,
:http_opts,
:json_codec,
Expand Down
18 changes: 11 additions & 7 deletions lib/ex_aws/config/auth_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule ExAws.Config.AuthCache do

# http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

@refresh_lead_time 300_000
@default_refresh_lead_time 300_000
@instance_auth_key :aws_instance_auth

defmodule AuthConfigAdapter do
Expand Down Expand Up @@ -34,6 +34,8 @@ defmodule ExAws.Config.AuthCache do
end
end

def default_refresh_lead_time(), do: @default_refresh_lead_time

## Callbacks

def init(:ok) do
Expand Down Expand Up @@ -101,7 +103,7 @@ defmodule ExAws.Config.AuthCache do
end

defp refresh_auth_if_required([{_key, cached_auth}], config) do
if next_refresh_in(cached_auth) > 0 do
if next_refresh_in(cached_auth, config) > 0 do
cached_auth
else
GenServer.call(__MODULE__, {:refresh_auth, config}, 30_000)
Expand All @@ -118,7 +120,9 @@ defmodule ExAws.Config.AuthCache do
end

defp refresh_auth_if_stale([{_key, cached_auth}], config, ets) do
if next_refresh_in(cached_auth) > @refresh_lead_time do
IO.inspect config
IO.inspect next_refresh_in(cached_auth, config)
if next_refresh_in(cached_auth, config) > config.auth_cache_refresh_lead_time do
# we still have a valid auth token, so simply return that
cached_auth
else
Expand All @@ -131,11 +135,11 @@ defmodule ExAws.Config.AuthCache do
defp refresh_auth_now(config, ets) do
auth = ExAws.InstanceMeta.security_credentials(config)
:ets.insert(ets, {@instance_auth_key, auth})
Process.send_after(__MODULE__, {:refresh_auth, config}, next_refresh_in(auth))
Process.send_after(__MODULE__, {:refresh_auth, config}, next_refresh_in(auth, config))
auth
end

defp next_refresh_in(%{expiration: expiration}) do
defp next_refresh_in(%{expiration: expiration}, config) do
try do
expires_in_ms =
expiration
Expand All @@ -144,11 +148,11 @@ defmodule ExAws.Config.AuthCache do

# refresh lead_time before auth expires, unless the time has passed
# otherwise refresh needed now
max(0, expires_in_ms - @refresh_lead_time)
max(0, expires_in_ms - config.auth_cache_refresh_lead_time)
rescue
_e -> 0
end
end

defp next_refresh_in(_), do: 0
defp next_refresh_in(_, _config), do: 0
end
1 change: 1 addition & 0 deletions lib/ex_aws/config/defaults.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule ExAws.Config.Defaults do
"""

@common %{
auth_cache_refresh_lead_time: ExAws.Config.AuthCache.default_refresh_lead_time(),
access_key_id: [{:system, "AWS_ACCESS_KEY_ID"}, :instance_role],
secret_access_key: [{:system, "AWS_SECRET_ACCESS_KEY"}, :instance_role],
http_client: ExAws.Request.Hackney,
Expand Down
1 change: 1 addition & 0 deletions lib/ex_aws/instance_meta.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ defmodule ExAws.InstanceMeta do
end

def instance_role(config) do
IO.inspect "HERE"
ExAws.InstanceMeta.request(config, @meta_path_root <> "/iam/security-credentials/")
end

Expand Down
85 changes: 60 additions & 25 deletions test/ex_aws/auth/auth_cache_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,13 @@ defmodule ExAws.AuthCacheTest do
test "using adapter does not leak dirty cache" do
parent = self()

op = %ExAws.Operation.S3{
body: "",
bucket: "",
headers: %{},
http_method: :get,
params: [],
parser: & &1,
path: "/",
resource: "",
service: :s3,
stream_builder: nil
}

spawn(fn ->
ExAws.Request.HttpMock
|> expect(:request, fn _method, _url, _body, _headers, _opts ->
@response
end)

result = ExAws.request(op, @config)
result = ExAws.request(op(), @config)
send(parent, result)
end)

Expand All @@ -67,11 +54,56 @@ defmodule ExAws.AuthCacheTest do
end)

Process.sleep(100)
assert ExAws.request(op, @config) == @response
assert ExAws.request(op(), @config) == @response

assert_receive @response, 1000
end

test "overriding refresh lead time" do

Check failure on line 62 in test/ex_aws/auth/auth_cache_test.exs

View workflow job for this annotation

GitHub Actions / build (26.x, 1.16.x)

test overriding refresh lead time (ExAws.AuthCacheTest)

Check failure on line 62 in test/ex_aws/auth/auth_cache_test.exs

View workflow job for this annotation

GitHub Actions / build (25.x, 1.16.x)

test overriding refresh lead time (ExAws.AuthCacheTest)

Check failure on line 62 in test/ex_aws/auth/auth_cache_test.exs

View workflow job for this annotation

GitHub Actions / build (25.x, 1.15.x)

test overriding refresh lead time (ExAws.AuthCacheTest)

Check failure on line 62 in test/ex_aws/auth/auth_cache_test.exs

View workflow job for this annotation

GitHub Actions / build (24.x, 1.16.x)

test overriding refresh lead time (ExAws.AuthCacheTest)

Check failure on line 62 in test/ex_aws/auth/auth_cache_test.exs

View workflow job for this annotation

GitHub Actions / build (24.x, 1.15.x)

test overriding refresh lead time (ExAws.AuthCacheTest)

Check failure on line 62 in test/ex_aws/auth/auth_cache_test.exs

View workflow job for this annotation

GitHub Actions / build (24.x, 1.14.x)

test overriding refresh lead time (ExAws.AuthCacheTest)
defmodule RandomAdapter do
@moduledoc false

@behaviour ExAws.Config.AuthCache.AuthConfigAdapter

def adapt_auth_config(_config, _profile, _expiration) do
%{
access_key_id: Base.encode64(:crypto.strong_rand_bytes(20)),
secret_access_key: Base.encode64(:crypto.strong_rand_bytes(20))
}
end
end

config = [
http_client: ExAws.Request.HttpMock,
access_key_id: :instance_role,
secret_access_key: :instance_role
]

Application.put_env(:ex_aws, :awscli_auth_adapter, RandomAdapter)

parent = self()

ExAws.Request.HttpMock
|> expect(:request, 5,
fn :get, "http://169.254.169.254/latest/meta-data/iam/security-credentials/", _body, _headers, _opts ->
{:ok, %{status_code: 200, body: "dummy-role"}}
:get, _url, _body, headers, _opts ->
send(parent, headers)
@response
end)

assert ExAws.request(op(), config) == @response
assert ExAws.request(op(), config) == @response
assert ExAws.request(op(), [{:auth_cache_refresh_lead_time, 1_000_000_000_000} | config]) == @response
assert_receive headers1, 1000
assert_receive headers2, 1000
assert_receive headers3, 1000

IO.inspect(headers1)
IO.inspect(headers2)
IO.inspect(headers3)
end

test "using adapter retries when there is an error" do
# The flaky adapter simulates failures on the adapter side
# for a few a tries and then returns a successful response.
Expand Down Expand Up @@ -108,25 +140,28 @@ defmodule ExAws.AuthCacheTest do
FlakyAdapter.init()
Application.put_env(:ex_aws, :awscli_auth_adapter, FlakyAdapter)

op = %ExAws.Operation.S3{
ExAws.Request.HttpMock
|> expect(:request, fn _method, _url, _body, _headers, _opts ->
@response
end)

Process.sleep(100)
assert ExAws.request(op(), @config) == @response
end

defp op do
%ExAws.Operation.S3{
body: "",
bucket: "",
headers: %{},
http_method: :get,
params: [],
parser: & &1,
parser: fn x -> x end,
path: "/",
resource: "",
service: :s3,
stream_builder: nil
}

ExAws.Request.HttpMock
|> expect(:request, fn _method, _url, _body, _headers, _opts ->
@response
end)

Process.sleep(100)
assert ExAws.request(op, @config) == @response
end

end

0 comments on commit 4a0f3ab

Please sign in to comment.