755 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Erlang
		
	
	
	
			
		
		
	
	
			755 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Erlang
		
	
	
	
| -module(rabbitmq_aws_tests).
 | |
| 
 | |
| -include_lib("eunit/include/eunit.hrl").
 | |
| 
 | |
| -include("rabbitmq_aws.hrl").
 | |
| 
 | |
| init_test_() ->
 | |
|     {foreach,
 | |
|         fun() ->
 | |
|             os:putenv("AWS_DEFAULT_REGION", "us-west-3"),
 | |
|             meck:new(rabbitmq_aws_config, [passthrough])
 | |
|         end,
 | |
|         fun(_) ->
 | |
|             os:unsetenv("AWS_DEFAULT_REGION"),
 | |
|             meck:unload(rabbitmq_aws_config)
 | |
|         end,
 | |
|         [
 | |
|             {"ok", fun() ->
 | |
|                 os:putenv("AWS_ACCESS_KEY_ID", "Sésame"),
 | |
|                 os:putenv("AWS_SECRET_ACCESS_KEY", "ouvre-toi"),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-west-3"),
 | |
|                 rabbitmq_aws:refresh_credentials(),
 | |
|                 {ok, State} = gen_server:call(Pid, get_state),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 os:unsetenv("AWS_ACCESS_KEY_ID"),
 | |
|                 os:unsetenv("AWS_SECRET_ACCESS_KEY"),
 | |
|                 Expectation =
 | |
|                     {state, "Sésame", "ouvre-toi", undefined, undefined, "us-west-3", undefined,
 | |
|                         undefined},
 | |
|                 ?assertEqual(Expectation, State)
 | |
|             end},
 | |
|             {"error", fun() ->
 | |
|                 meck:expect(rabbitmq_aws_config, credentials, fun() -> {error, test_result} end),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-west-3"),
 | |
|                 rabbitmq_aws:refresh_credentials(),
 | |
|                 {ok, State} = gen_server:call(Pid, get_state),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 Expectation =
 | |
|                     {state, undefined, undefined, undefined, undefined, "us-west-3", undefined,
 | |
|                         test_result},
 | |
|                 ?assertEqual(Expectation, State),
 | |
|                 meck:validate(rabbitmq_aws_config)
 | |
|             end}
 | |
|         ]}.
 | |
| 
 | |
| terminate_test() ->
 | |
|     ?assertEqual(ok, rabbitmq_aws:terminate(foo, bar)).
 | |
| 
 | |
| code_change_test() ->
 | |
|     ?assertEqual({ok, {state, denial}}, rabbitmq_aws:code_change(foo, bar, {state, denial})).
 | |
| 
 | |
| endpoint_test_() ->
 | |
|     [
 | |
|         {"specified", fun() ->
 | |
|             Region = "us-east-3",
 | |
|             Service = "dynamodb",
 | |
|             Path = "/",
 | |
|             Host = "localhost:32767",
 | |
|             Expectation = "https://localhost:32767/",
 | |
|             ?assertEqual(
 | |
|                 Expectation, rabbitmq_aws:endpoint(#state{region = Region}, Host, Service, Path)
 | |
|             )
 | |
|         end},
 | |
|         {"unspecified", fun() ->
 | |
|             Region = "us-east-3",
 | |
|             Service = "dynamodb",
 | |
|             Path = "/",
 | |
|             Host = undefined,
 | |
|             Expectation = "https://dynamodb.us-east-3.amazonaws.com/",
 | |
|             ?assertEqual(
 | |
|                 Expectation, rabbitmq_aws:endpoint(#state{region = Region}, Host, Service, Path)
 | |
|             )
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| endpoint_host_test_() ->
 | |
|     [
 | |
|         {"dynamodb service", fun() ->
 | |
|             Expectation = "dynamodb.us-west-2.amazonaws.com",
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:endpoint_host("us-west-2", "dynamodb"))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| cn_endpoint_host_test_() ->
 | |
|     [
 | |
|         {"s3", fun() ->
 | |
|             Expectation = "s3.cn-north-1.amazonaws.com.cn",
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:endpoint_host("cn-north-1", "s3"))
 | |
|         end},
 | |
|         {"s3", fun() ->
 | |
|             Expectation = "s3.cn-northwest-1.amazonaws.com.cn",
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:endpoint_host("cn-northwest-1", "s3"))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| expired_credentials_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             meck:new(calendar, [passthrough, unstick]),
 | |
|             [calendar]
 | |
|         end,
 | |
|         fun meck:unload/1,
 | |
|         [
 | |
|             {"true", fun() ->
 | |
|                 Value = {{2016, 4, 1}, {12, 0, 0}},
 | |
|                 Expectation = true,
 | |
|                 meck:expect(calendar, local_time_to_universal_time_dst, fun(_) ->
 | |
|                     [{{2016, 4, 1}, {12, 0, 0}}]
 | |
|                 end),
 | |
|                 ?assertEqual(Expectation, rabbitmq_aws:expired_credentials(Value)),
 | |
|                 meck:validate(calendar)
 | |
|             end},
 | |
|             {"false", fun() ->
 | |
|                 Value = {{2016, 5, 1}, {16, 30, 0}},
 | |
|                 Expectation = false,
 | |
|                 meck:expect(calendar, local_time_to_universal_time_dst, fun(_) ->
 | |
|                     [{{2016, 4, 1}, {12, 0, 0}}]
 | |
|                 end),
 | |
|                 ?assertEqual(Expectation, rabbitmq_aws:expired_credentials(Value)),
 | |
|                 meck:validate(calendar)
 | |
|             end},
 | |
|             {"undefined", fun() ->
 | |
|                 ?assertEqual(false, rabbitmq_aws:expired_credentials(undefined))
 | |
|             end}
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| format_response_test_() ->
 | |
|     [
 | |
|         {"ok", fun() ->
 | |
|             Response =
 | |
|                 {ok, {
 | |
|                     {"HTTP/1.1", 200, "Ok"}, [{"Content-Type", "text/xml"}], "<test>Value</test>"
 | |
|                 }},
 | |
|             Expectation = {ok, {[{"Content-Type", "text/xml"}], [{"test", "Value"}]}},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:format_response(Response))
 | |
|         end},
 | |
|         {"error", fun() ->
 | |
|             Response =
 | |
|                 {ok, {
 | |
|                     {"HTTP/1.1", 500, "Internal Server Error"},
 | |
|                     [{"Content-Type", "text/xml"}],
 | |
|                     "<error>Boom</error>"
 | |
|                 }},
 | |
|             Expectation =
 | |
|                 {error, "Internal Server Error",
 | |
|                     {[{"Content-Type", "text/xml"}], [{"error", "Boom"}]}},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:format_response(Response))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| gen_server_call_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             % We explicitely set a few defaults, in case the caller has
 | |
|             % something in ~/.aws.
 | |
|             os:putenv("AWS_DEFAULT_REGION", "us-west-3"),
 | |
|             os:putenv("AWS_ACCESS_KEY_ID", "Sésame"),
 | |
|             os:putenv("AWS_SECRET_ACCESS_KEY", "ouvre-toi"),
 | |
|             meck:new(httpc, []),
 | |
|             [httpc]
 | |
|         end,
 | |
|         fun(Mods) ->
 | |
|             meck:unload(Mods),
 | |
|             os:unsetenv("AWS_DEFAULT_REGION"),
 | |
|             os:unsetenv("AWS_ACCESS_KEY_ID"),
 | |
|             os:unsetenv("AWS_SECRET_ACCESS_KEY")
 | |
|         end,
 | |
|         [
 | |
|             {
 | |
|                 "request",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "AKIDEXAMPLE",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                         region = "us-east-1"
 | |
|                     },
 | |
|                     Service = "ec2",
 | |
|                     Method = get,
 | |
|                     Headers = [],
 | |
|                     Path = "/?Action=DescribeTags&Version=2015-10-01",
 | |
|                     Body = "",
 | |
|                     Options = [],
 | |
|                     Host = undefined,
 | |
|                     meck:expect(
 | |
|                         httpc,
 | |
|                         request,
 | |
|                         fun(
 | |
|                             get,
 | |
|                             {"https://ec2.us-east-1.amazonaws.com/?Action=DescribeTags&Version=2015-10-01",
 | |
|                                 _Headers},
 | |
|                             _Options,
 | |
|                             []
 | |
|                         ) ->
 | |
|                             {ok, {
 | |
|                                 {"HTTP/1.0", 200, "OK"},
 | |
|                                 [{"content-type", "application/json"}],
 | |
|                                 "{\"pass\": true}"
 | |
|                             }}
 | |
|                         end
 | |
|                     ),
 | |
|                     Expectation =
 | |
|                         {reply, {ok, {[{"content-type", "application/json"}], [{"pass", true}]}},
 | |
|                             State},
 | |
|                     Result = rabbitmq_aws:handle_call(
 | |
|                         {request, Service, Method, Headers, Path, Body, Options, Host}, eunit, State
 | |
|                     ),
 | |
|                     ?assertEqual(Expectation, Result),
 | |
|                     meck:validate(httpc)
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "get_state",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "AKIDEXAMPLE",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                         region = "us-east-1"
 | |
|                     },
 | |
|                     ?assertEqual(
 | |
|                         {reply, {ok, State}, State},
 | |
|                         rabbitmq_aws:handle_call(get_state, eunit, State)
 | |
|                     )
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "refresh_credentials",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "AKIDEXAMPLE",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                         region = "us-east-1"
 | |
|                     },
 | |
|                     State2 = #state{
 | |
|                         access_key = "AKIDEXAMPLE2",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY2",
 | |
|                         region = "us-east-1",
 | |
|                         security_token =
 | |
|                             "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/L2",
 | |
|                         expiration = calendar:local_time()
 | |
|                     },
 | |
|                     meck:new(rabbitmq_aws_config, [passthrough]),
 | |
|                     meck:expect(
 | |
|                         rabbitmq_aws_config,
 | |
|                         credentials,
 | |
|                         fun() ->
 | |
|                             {ok, State2#state.access_key, State2#state.secret_access_key,
 | |
|                                 State2#state.expiration, State2#state.security_token}
 | |
|                         end
 | |
|                     ),
 | |
|                     ?assertEqual(
 | |
|                         {reply, ok, State2},
 | |
|                         rabbitmq_aws:handle_call(refresh_credentials, eunit, State)
 | |
|                     ),
 | |
|                     meck:validate(rabbitmq_aws_config),
 | |
|                     meck:unload(rabbitmq_aws_config)
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "set_credentials",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "AKIDEXAMPLE",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                         region = "us-west-3"
 | |
|                     },
 | |
|                     ?assertEqual(
 | |
|                         {reply, ok, State},
 | |
|                         rabbitmq_aws:handle_call(
 | |
|                             {set_credentials, State#state.access_key,
 | |
|                                 State#state.secret_access_key},
 | |
|                             eunit,
 | |
|                             #state{region = "us-west-3"}
 | |
|                         )
 | |
|                     )
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "set_region",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "Sésame",
 | |
|                         secret_access_key = "ouvre-toi",
 | |
|                         region = "us-east-5"
 | |
|                     },
 | |
|                     ?assertEqual(
 | |
|                         {reply, ok, State},
 | |
|                         rabbitmq_aws:handle_call({set_region, "us-east-5"}, eunit, #state{
 | |
|                             access_key = "Sésame",
 | |
|                             secret_access_key = "ouvre-toi"
 | |
|                         })
 | |
|                     )
 | |
|                 end
 | |
|             }
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| get_content_type_test_() ->
 | |
|     [
 | |
|         {"from headers caps", fun() ->
 | |
|             Headers = [{"Content-Type", "text/xml"}],
 | |
|             Expectation = {"text", "xml"},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:get_content_type(Headers))
 | |
|         end},
 | |
|         {"from headers lower", fun() ->
 | |
|             Headers = [{"content-type", "text/xml"}],
 | |
|             Expectation = {"text", "xml"},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:get_content_type(Headers))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| has_credentials_test_() ->
 | |
|     [
 | |
|         {"true", fun() ->
 | |
|             ?assertEqual(true, rabbitmq_aws:has_credentials(#state{access_key = "TESTVALUE1"}))
 | |
|         end},
 | |
|         {"false", fun() ->
 | |
|             ?assertEqual(false, rabbitmq_aws:has_credentials(#state{error = "ERROR"}))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| local_time_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             meck:new(calendar, [passthrough, unstick]),
 | |
|             [calendar]
 | |
|         end,
 | |
|         fun meck:unload/1,
 | |
|         [
 | |
|             {"value", fun() ->
 | |
|                 Value = {{2016, 5, 1}, {12, 0, 0}},
 | |
|                 meck:expect(calendar, local_time_to_universal_time_dst, fun(_) -> [Value] end),
 | |
|                 ?assertEqual(Value, rabbitmq_aws:local_time()),
 | |
|                 meck:validate(calendar)
 | |
|             end}
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| maybe_decode_body_test_() ->
 | |
|     [
 | |
|         {"application/x-amz-json-1.0", fun() ->
 | |
|             ContentType = {"application", "x-amz-json-1.0"},
 | |
|             Body = "{\"test\": true}",
 | |
|             Expectation = [{"test", true}],
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:maybe_decode_body(ContentType, Body))
 | |
|         end},
 | |
|         {"application/json", fun() ->
 | |
|             ContentType = {"application", "json"},
 | |
|             Body = "{\"test\": true}",
 | |
|             Expectation = [{"test", true}],
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:maybe_decode_body(ContentType, Body))
 | |
|         end},
 | |
|         {"text/xml", fun() ->
 | |
|             ContentType = {"text", "xml"},
 | |
|             Body = "<test><node>value</node></test>",
 | |
|             Expectation = [{"test", [{"node", "value"}]}],
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:maybe_decode_body(ContentType, Body))
 | |
|         end},
 | |
|         {"text/html [unsupported]", fun() ->
 | |
|             ContentType = {"text", "html"},
 | |
|             Body = "<html><head></head><body></body></html>",
 | |
|             ?assertEqual(Body, rabbitmq_aws:maybe_decode_body(ContentType, Body))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| parse_content_type_test_() ->
 | |
|     [
 | |
|         {"application/x-amz-json-1.0", fun() ->
 | |
|             Expectation = {"application", "x-amz-json-1.0"},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:parse_content_type("application/x-amz-json-1.0"))
 | |
|         end},
 | |
|         {"application/xml", fun() ->
 | |
|             Expectation = {"application", "xml"},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:parse_content_type("application/xml"))
 | |
|         end},
 | |
|         {"text/xml;charset=UTF-8", fun() ->
 | |
|             Expectation = {"text", "xml"},
 | |
|             ?assertEqual(Expectation, rabbitmq_aws:parse_content_type("text/xml"))
 | |
|         end}
 | |
|     ].
 | |
| 
 | |
| perform_request_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             meck:new(httpc, []),
 | |
|             meck:new(rabbitmq_aws_config, []),
 | |
|             [httpc, rabbitmq_aws_config]
 | |
|         end,
 | |
|         fun meck:unload/1,
 | |
|         [
 | |
|             {
 | |
|                 "has_credentials true",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "AKIDEXAMPLE",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                         region = "us-east-1"
 | |
|                     },
 | |
|                     Service = "ec2",
 | |
|                     Method = get,
 | |
|                     Headers = [],
 | |
|                     Path = "/?Action=DescribeTags&Version=2015-10-01",
 | |
|                     Body = "",
 | |
|                     Options = [],
 | |
|                     Host = undefined,
 | |
|                     ExpectURI =
 | |
|                         "https://ec2.us-east-1.amazonaws.com/?Action=DescribeTags&Version=2015-10-01",
 | |
|                     meck:expect(
 | |
|                         httpc,
 | |
|                         request,
 | |
|                         fun(get, {URI, _Headers}, _Options, []) ->
 | |
|                             case URI of
 | |
|                                 ExpectURI ->
 | |
|                                     {ok, {
 | |
|                                         {"HTTP/1.0", 200, "OK"},
 | |
|                                         [{"content-type", "application/json"}],
 | |
|                                         "{\"pass\": true}"
 | |
|                                     }};
 | |
|                                 _ ->
 | |
|                                     {ok,
 | |
|                                         {{"HTTP/1.0", 400, "RequestFailure",
 | |
|                                             [{"content-type", "application/json"}],
 | |
|                                             "{\"pass\": false}"}}}
 | |
|                             end
 | |
|                         end
 | |
|                     ),
 | |
|                     Expectation = {
 | |
|                         {ok, {[{"content-type", "application/json"}], [{"pass", true}]}}, State
 | |
|                     },
 | |
|                     Result = rabbitmq_aws:perform_request(
 | |
|                         State, Service, Method, Headers, Path, Body, Options, Host
 | |
|                     ),
 | |
|                     ?assertEqual(Expectation, Result),
 | |
|                     meck:validate(httpc)
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "has_credentials false",
 | |
|                 fun() ->
 | |
|                     State = #state{region = "us-east-1"},
 | |
|                     Service = "ec2",
 | |
|                     Method = get,
 | |
|                     Headers = [],
 | |
|                     Path = "/?Action=DescribeTags&Version=2015-10-01",
 | |
|                     Body = "",
 | |
|                     Options = [],
 | |
|                     Host = undefined,
 | |
|                     meck:expect(httpc, request, fun(get, {_URI, _Headers}, _Options, []) ->
 | |
|                         {ok, {
 | |
|                             {"HTTP/1.0", 400, "RequestFailure"},
 | |
|                             [{"content-type", "application/json"}],
 | |
|                             "{\"pass\": false}"
 | |
|                         }}
 | |
|                     end),
 | |
|                     Expectation = {{error, {credentials, State#state.error}}, State},
 | |
|                     Result = rabbitmq_aws:perform_request(
 | |
|                         State, Service, Method, Headers, Path, Body, Options, Host
 | |
|                     ),
 | |
|                     ?assertEqual(Expectation, Result),
 | |
|                     meck:validate(httpc)
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "has expired credentials",
 | |
|                 fun() ->
 | |
|                     State = #state{
 | |
|                         access_key = "AKIDEXAMPLE",
 | |
|                         secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                         region = "us-east-1",
 | |
|                         security_token =
 | |
|                             "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/L",
 | |
|                         expiration = {{1973, 1, 1}, {10, 20, 30}}
 | |
|                     },
 | |
|                     Service = "ec2",
 | |
|                     Method = get,
 | |
|                     Headers = [],
 | |
|                     Path = "/?Action=DescribeTags&Version=2015-10-01",
 | |
|                     Body = "",
 | |
|                     Options = [],
 | |
|                     Host = undefined,
 | |
|                     meck:expect(rabbitmq_aws_config, credentials, fun() -> {error, unit_test} end),
 | |
|                     Expectation = {{error, {credentials, "Credentials expired!"}}, State#state{
 | |
|                         error = "Credentials expired!"
 | |
|                     }},
 | |
|                     Result = rabbitmq_aws:perform_request(
 | |
|                         State, Service, Method, Headers, Path, Body, Options, Host
 | |
|                     ),
 | |
|                     ?assertEqual(Expectation, Result),
 | |
|                     meck:validate(rabbitmq_aws_config)
 | |
|                 end
 | |
|             },
 | |
|             {
 | |
|                 "creds_error",
 | |
|                 fun() ->
 | |
|                     State = #state{error = unit_test},
 | |
|                     Expectation = {{error, {credentials, State#state.error}}, State},
 | |
|                     ?assertEqual(Expectation, rabbitmq_aws:perform_request_creds_error(State))
 | |
|                 end
 | |
|             }
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| sign_headers_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             meck:new(calendar, [passthrough, unstick]),
 | |
|             [calendar]
 | |
|         end,
 | |
|         fun meck:unload/1,
 | |
|         [
 | |
|             {"with security token", fun() ->
 | |
|                 Value = {{2016, 5, 1}, {12, 0, 0}},
 | |
|                 meck:expect(calendar, local_time_to_universal_time_dst, fun(_) -> [Value] end),
 | |
|                 State = #state{
 | |
|                     access_key = "AKIDEXAMPLE",
 | |
|                     secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
 | |
|                     security_token =
 | |
|                         "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/L",
 | |
|                     region = "us-east-1"
 | |
|                 },
 | |
|                 Service = "ec2",
 | |
|                 Method = get,
 | |
|                 Headers = [],
 | |
|                 Body = "",
 | |
|                 URI = "http://ec2.us-east-1.amazonaws.com/?Action=DescribeTags&Version=2015-10-01",
 | |
|                 Expectation = [
 | |
|                     {"authorization",
 | |
|                         "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20160501/us-east-1/ec2/aws4_request, SignedHeaders=content-length;date;host;x-amz-content-sha256;x-amz-security-token, Signature=62d10b4897f7d05e4454b75895b5e372f6c2eb6997943cd913680822e94c6999"},
 | |
|                     {"content-length", "0"},
 | |
|                     {"date", "20160501T120000Z"},
 | |
|                     {"host", "ec2.us-east-1.amazonaws.com"},
 | |
|                     {"x-amz-content-sha256",
 | |
|                         "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
 | |
|                     {"x-amz-security-token",
 | |
|                         "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/L"}
 | |
|                 ],
 | |
|                 ?assertEqual(
 | |
|                     Expectation,
 | |
|                     rabbitmq_aws:sign_headers(State, Service, Method, URI, Headers, Body)
 | |
|                 ),
 | |
|                 meck:validate(calendar)
 | |
|             end}
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| api_get_request_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             meck:new(httpc, []),
 | |
|             meck:new(rabbitmq_aws_config, []),
 | |
|             [httpc, rabbitmq_aws_config]
 | |
|         end,
 | |
|         fun meck:unload/1,
 | |
|         [
 | |
|             {"AWS service API request succeeded", fun() ->
 | |
|                 State = #state{
 | |
|                     access_key = "ExpiredKey",
 | |
|                     secret_access_key = "ExpiredAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{3016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
|                 meck:expect(
 | |
|                     httpc,
 | |
|                     request,
 | |
|                     4,
 | |
|                     {ok, {
 | |
|                         {"HTTP/1.0", 200, "OK"},
 | |
|                         [{"content-type", "application/json"}],
 | |
|                         "{\"data\": \"value\"}"
 | |
|                     }}
 | |
|                 ),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 rabbitmq_aws:set_credentials(State),
 | |
|                 Result = rabbitmq_aws:api_get_request("AWS", "API"),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual({ok, [{"data", "value"}]}, Result),
 | |
|                 meck:validate(httpc)
 | |
|             end},
 | |
|             {"AWS service API request failed - credentials", fun() ->
 | |
|                 meck:expect(rabbitmq_aws_config, credentials, 0, {error, undefined}),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 Result = rabbitmq_aws:api_get_request("AWS", "API"),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual({error, credentials}, Result)
 | |
|             end},
 | |
|             {"AWS service API request failed - API error with persistent failure", fun() ->
 | |
|                 State = #state{
 | |
|                     access_key = "ExpiredKey",
 | |
|                     secret_access_key = "ExpiredAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{3016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
|                 meck:expect(httpc, request, 4, {error, "network error"}),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 rabbitmq_aws:set_credentials(State),
 | |
|                 Result = rabbitmq_aws:api_get_request_with_retries("AWS", "API", 3, 1),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual({error, "AWS service is unavailable"}, Result),
 | |
|                 meck:validate(httpc)
 | |
|             end},
 | |
|             {"AWS service API request succeeded after a transient error", fun() ->
 | |
|                 State = #state{
 | |
|                     access_key = "ExpiredKey",
 | |
|                     secret_access_key = "ExpiredAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{3016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
|                 meck:expect(
 | |
|                     httpc,
 | |
|                     request,
 | |
|                     4,
 | |
|                     meck:seq([
 | |
|                         {error, "network error"},
 | |
|                         {ok, {
 | |
|                             {"HTTP/1.0", 500, "OK"},
 | |
|                             [{"content-type", "application/json"}],
 | |
|                             "{\"error\": \"server error\"}"
 | |
|                         }},
 | |
|                         {ok, {
 | |
|                             {"HTTP/1.0", 200, "OK"},
 | |
|                             [{"content-type", "application/json"}],
 | |
|                             "{\"data\": \"value\"}"
 | |
|                         }}
 | |
|                     ])
 | |
|                 ),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 rabbitmq_aws:set_credentials(State),
 | |
|                 Result = rabbitmq_aws:api_get_request_with_retries("AWS", "API", 3, 1),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual({ok, [{"data", "value"}]}, Result),
 | |
|                 meck:validate(httpc)
 | |
|             end}
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| ensure_credentials_valid_test_() ->
 | |
|     {
 | |
|         foreach,
 | |
|         fun() ->
 | |
|             meck:new(rabbitmq_aws_config, []),
 | |
|             [rabbitmq_aws_config]
 | |
|         end,
 | |
|         fun meck:unload/1,
 | |
|         [
 | |
|             {"expired credentials are refreshed", fun() ->
 | |
|                 State = #state{
 | |
|                     access_key = "ExpiredKey",
 | |
|                     secret_access_key = "ExpiredAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{2016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
|                 State2 = #state{
 | |
|                     access_key = "NewKey",
 | |
|                     secret_access_key = "NewAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{3016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
| 
 | |
|                 meck:expect(
 | |
|                     rabbitmq_aws_config,
 | |
|                     credentials,
 | |
|                     fun() ->
 | |
|                         {ok, State2#state.access_key, State2#state.secret_access_key,
 | |
|                             State2#state.expiration, State2#state.security_token}
 | |
|                     end
 | |
|                 ),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 rabbitmq_aws:set_credentials(State),
 | |
|                 Result = rabbitmq_aws:ensure_credentials_valid(),
 | |
|                 Credentials = gen_server:call(Pid, get_state),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual(ok, Result),
 | |
|                 ?assertEqual(Credentials, {ok, State2}),
 | |
|                 meck:validate(rabbitmq_aws_config)
 | |
|             end},
 | |
|             {"valid credentials are returned", fun() ->
 | |
|                 State = #state{
 | |
|                     access_key = "GoodKey",
 | |
|                     secret_access_key = "GoodAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{3016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 rabbitmq_aws:set_credentials(State),
 | |
|                 Result = rabbitmq_aws:ensure_credentials_valid(),
 | |
|                 Credentials = gen_server:call(Pid, get_state),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual(ok, Result),
 | |
|                 ?assertEqual(Credentials, {ok, State}),
 | |
|                 meck:validate(rabbitmq_aws_config)
 | |
|             end},
 | |
|             {"load credentials if missing", fun() ->
 | |
|                 State = #state{
 | |
|                     access_key = "GoodKey",
 | |
|                     secret_access_key = "GoodAccessKey",
 | |
|                     region = "us-east-1",
 | |
|                     expiration = {{3016, 4, 1}, {12, 0, 0}}
 | |
|                 },
 | |
|                 meck:expect(
 | |
|                     rabbitmq_aws_config,
 | |
|                     credentials,
 | |
|                     fun() ->
 | |
|                         {ok, State#state.access_key, State#state.secret_access_key,
 | |
|                             State#state.expiration, State#state.security_token}
 | |
|                     end
 | |
|                 ),
 | |
|                 {ok, Pid} = rabbitmq_aws:start_link(),
 | |
|                 rabbitmq_aws:set_region("us-east-1"),
 | |
|                 Result = rabbitmq_aws:ensure_credentials_valid(),
 | |
|                 Credentials = gen_server:call(Pid, get_state),
 | |
|                 ok = gen_server:stop(Pid),
 | |
|                 ?assertEqual(ok, Result),
 | |
|                 ?assertEqual(Credentials, {ok, State}),
 | |
|                 meck:validate(rabbitmq_aws_config)
 | |
|             end}
 | |
|         ]
 | |
|     }.
 | |
| 
 | |
| expired_imdsv2_token_test_() ->
 | |
|     [
 | |
|         {"imdsv2 token is valid", fun() ->
 | |
|             [Value] = calendar:local_time_to_universal_time_dst(calendar:local_time()),
 | |
|             Now = calendar:datetime_to_gregorian_seconds(Value),
 | |
|             Imdsv2Token = #imdsv2token{token = "value", expiration = Now + 100},
 | |
|             ?assertEqual(false, rabbitmq_aws:expired_imdsv2_token(Imdsv2Token))
 | |
|         end},
 | |
|         {"imdsv2 token is expired", fun() ->
 | |
|             [Value] = calendar:local_time_to_universal_time_dst(calendar:local_time()),
 | |
|             Now = calendar:datetime_to_gregorian_seconds(Value),
 | |
|             Imdsv2Token = #imdsv2token{token = "value", expiration = Now - 100},
 | |
|             ?assertEqual(true, rabbitmq_aws:expired_imdsv2_token(Imdsv2Token))
 | |
|         end},
 | |
|         {"imdsv2 token is not yet initialized", fun() ->
 | |
|             ?assertEqual(true, rabbitmq_aws:expired_imdsv2_token(undefined))
 | |
|         end},
 | |
|         {"imdsv2 token is undefined", fun() ->
 | |
|             Imdsv2Token = #imdsv2token{token = undefined, expiration = undefined},
 | |
|             ?assertEqual(true, rabbitmq_aws:expired_imdsv2_token(Imdsv2Token))
 | |
|         end}
 | |
|     ].
 |