Current section
Files
Jump to
Current section
Files
src/erlcloud_route53.erl
%% Amazon Route 53
-module(erlcloud_route53).
-ifdef(TEST).
-export([describe_all/4, describe_all/5]).
-endif.
-type(aws_route53_delegation_set() :: proplists:proplist()).
-type(aws_route53_zone() :: proplists:proplist()).
-type(aws_route53_resourceset() :: proplists:proplist()).
-export([configure/2, configure/3, new/2, new/3]).
-export([describe_zone/1, describe_zone/2, describe_zone/3]).
-export([describe_zones/0, describe_zones/1, describe_zones/2,
describe_zones_all/0, describe_zones_all/1, describe_zones_all/2]).
-export([describe_resource_sets/1, describe_resource_sets/2,
describe_resource_sets/3, describe_resource_sets_all/1,
describe_resource_sets_all/2, describe_resource_sets_all/3]).
-export([describe_delegation_sets/0, describe_delegation_sets/1,
describe_delegation_sets/2, describe_delegation_sets_all/0,
describe_delegation_sets_all/1, describe_delegation_sets_all/2]).
-include("erlcloud.hrl").
-include("erlcloud_aws.hrl").
-define(API_VERSION, "2013-04-01").
-define(DEFAULT_MAX_RECORDS, 100).
-define(DESCRIBE_ZONES_PATH, "/ListHostedZonesResponse/HostedZones/HostedZone").
-define(DESCRIBE_ZONES_IS_TRUNCATED, "/ListHostedZonesResponse/IsTruncated").
-define(DESCRIBE_ZONES_NEXT_MARKER, "/ListHostedZonesResponse/NextMarker").
-define(DESCRIBE_ZONE_PATH, "/GetHostedZoneResponse/HostedZone").
-define(DESCRIBE_ZONE_VPCS_PATH, "/GetHostedZoneResponse/VPCs/VPC").
-define(DESCRIBE_ZONE_DS_PATH, "/GetHostedZoneResponse/DelegationSet").
-define(DESCRIBE_RS_PATH,
"/ListResourceRecordSetsResponse/ResourceRecordSets/ResourceRecordSet").
-define(DESCRIBE_RS_IS_TRUNCATED,
"/ListResourceRecordSetsResponse/IsTruncated").
-define(DESCRIBE_RS_NEXT_TYPE,
"/ListResourceRecordSetsResponse/NextRecordType").
-define(DESCRIBE_RS_NEXT_NAME,
"/ListResourceRecordSetsResponse/NextRecordName").
-define(DESCRIBE_RS_NEXT_ID,
"/ListResourceRecordSetsResponse/NextRecordIdentifier").
-define(DESCRIBE_DS_PATH,
"/ListReusableDelegationSetsResponse/DelegationSets/DelegationSet").
-define(DESCRIBE_DS_IS_TRUNCATED,
"/ListReusableDelegationSetsResponse/IsTruncated").
-define(DESCRIBE_DS_NEXT_MARKER,
"/ListReusableDelegationSetsResponse/NextMarker").
-spec new(string(), string()) -> aws_config().
new(AccessKeyID, SecretAccessKey) ->
#aws_config{access_key_id=AccessKeyID,
secret_access_key=SecretAccessKey}.
-spec new(string(), string(), string()) -> aws_config().
new(AccessKeyID, SecretAccessKey, Host) ->
#aws_config{access_key_id=AccessKeyID,
secret_access_key=SecretAccessKey,
route53_host=Host}.
-spec configure(string(), string()) -> ok.
configure(AccessKeyID, SecretAccessKey) ->
erlcloud_config:configure(AccessKeyID, SecretAccessKey, fun new/2).
-spec configure(string(), string(), string()) -> ok.
configure(AccessKeyID, SecretAccessKey, Host) ->
erlcloud_config:configure(AccessKeyID, SecretAccessKey, Host, fun new/3).
%% --------------------------------------------------------------------
%% @doc Describes provided zone using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_zone(ZoneId :: string()) ->
{ok, list(aws_route53_zone())} |
{error, term()}.
describe_zone(ZoneId) ->
describe_zone(ZoneId, erlcloud_aws:default_config()).
%% --------------------------------------------------------------------
%% @doc Describes provided zone using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_zone(ZoneId :: string(),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_zone())} |
{error, term()}.
describe_zone(ZoneId, AwsConfig) ->
describe_zone(ZoneId, [], AwsConfig).
%% --------------------------------------------------------------------
%% @doc Describes provided zone using provided config + AWS options
%% @end
%% --------------------------------------------------------------------
-spec describe_zone(ZoneId :: string(),
Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_zone())} |
{error, term()}.
describe_zone(ZoneId, Options, Config) when is_list(Options),
is_record(Config, aws_config) ->
do_describe_zone(ZoneId, Options, Config).
%% --------------------------------------------------------------------
%% @doc Describes all zones using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_zones() ->
{ok, list(aws_route53_zone())} |
{ok, list(aws_route53_zone()), string()} |
{error, term()}.
describe_zones() ->
describe_zones(erlcloud_aws:default_config()).
%% --------------------------------------------------------------------
%% @doc Describes all zones using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_zones(AwsConfig :: aws_config()) ->
{ok, list(aws_route53_zone())} |
{ok, list(aws_route53_zone()), string()} |
{error, term()}.
describe_zones(AwsConfig) ->
describe_zones([], AwsConfig).
%% --------------------------------------------------------------------
%% @doc Describes all zones using provided config + AWS options
%% @end
%% --------------------------------------------------------------------
-spec describe_zones(Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_zone())} |
{ok, list(aws_route53_zone()), string()} |
{error, term()}.
describe_zones(Options, Config) when is_list(Options),
is_record(Config, aws_config) ->
do_describe_zones(Options, Config).
%% --------------------------------------------------------------------
%% @doc Describes all zones using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_zones_all() ->
{ok, list(aws_route53_zone())} |
{error, term()}.
describe_zones_all() ->
describe_all(fun describe_zones/2, [],
erlcloud_aws:default_config(), []).
%% --------------------------------------------------------------------
%% @doc Describes all zones using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_zones_all(AwsConfig :: aws_config()) ->
{ok, list(aws_route53_zone())} |
{error, term()}.
describe_zones_all(AwsConfig) ->
describe_all(fun describe_zones/2, [], AwsConfig, []).
%% --------------------------------------------------------------------
%% @doc Describes all zones using provided config + AWS options
%% @end
%% --------------------------------------------------------------------
-spec describe_zones_all(Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_zone())} |
{error, term()}.
describe_zones_all(Options, Config) when is_list(Options),
is_record(Config, aws_config) ->
describe_all(fun describe_zones/2, Options, Config, []).
%% --------------------------------------------------------------------
%% @doc Describes resource sets for a specific zone_id using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_resource_sets(ZoneId :: string()) ->
{ok, list(aws_route53_resourceset())} |
{ok, list(aws_route53_resourceset()), string()} |
{ok, list(aws_route53_resourceset()), {string(), string()}} |
{ok, list(aws_route53_resourceset()), {string(), string(), string()}} |
{error, term()}.
describe_resource_sets(ZoneId) ->
describe_resource_sets(ZoneId, erlcloud_aws:default_config()).
%% --------------------------------------------------------------------
%% @doc Describes resource sets for a specific zone_id using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_resource_sets(ZoneId :: string(),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_resourceset())} |
{ok, list(aws_route53_resourceset()), string()} |
{ok, list(aws_route53_resourceset()), {string(), string()}} |
{ok, list(aws_route53_resourceset()), {string(), string(), string()}} |
{error, term()}.
describe_resource_sets(ZoneId, AwsConfig) ->
describe_resource_sets(ZoneId, [], AwsConfig).
%% --------------------------------------------------------------------
%% @doc Describes resource sets with provided aws_config and
%% ZoneID with a list of AWS options
%% @end
%% --------------------------------------------------------------------
-spec describe_resource_sets(ZoneID :: string(),
Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_resourceset())} |
{ok, list(aws_route53_resourceset()), string()} |
{ok, list(aws_route53_resourceset()), {string(), string()}} |
{ok, list(aws_route53_resourceset()), {string(), string(), string()}} |
{error, term()}.
describe_resource_sets(ZoneId, Options, AwsConfig) ->
do_describe_resource_sets(ZoneId, Options, AwsConfig).
%% --------------------------------------------------------------------
%% @doc Describes resource sets for a specific zone_id using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_resource_sets_all(ZoneId :: string()) ->
{ok, list(aws_route53_resourceset())} |
{error, term()}.
describe_resource_sets_all(ZoneId) ->
describe_all(fun describe_resource_sets/3, [ZoneId], [],
erlcloud_aws:default_config(), []).
%% --------------------------------------------------------------------
%% @doc Describes resource sets for a specific zone_id using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_resource_sets_all(ZoneId :: string(),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_resourceset())} |
{error, term()}.
describe_resource_sets_all(ZoneId, AwsConfig) ->
describe_all(fun describe_resource_sets/3, [ZoneId], [], AwsConfig, []).
%% --------------------------------------------------------------------
%% @doc Describes resource sets with provided aws_config and
%% ZoneID with a list of AWS options
%% @end
%% --------------------------------------------------------------------
-spec describe_resource_sets_all(ZoneID :: string(),
Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_resourceset())} |
{error, term()}.
describe_resource_sets_all(ZoneId, Options, AwsConfig) ->
describe_all(fun describe_resource_sets/3, [ZoneId],
Options, AwsConfig, []).
%% --------------------------------------------------------------------
%% @doc Describes delegation sets using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_delegation_sets() ->
{ok, list(aws_route53_delegation_set())} |
{ok, list(aws_route53_delegation_set()), string()} |
{error, term()}.
describe_delegation_sets() ->
describe_delegation_sets(erlcloud_aws:default_config()).
%% --------------------------------------------------------------------
%% @doc Describes delegation sets using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_delegation_sets(AwsConfig :: aws_config()) ->
{ok, list(aws_route53_delegation_set())} |
{ok, list(aws_route53_delegation_set()), string()} |
{error, term()}.
describe_delegation_sets(AwsConfig) ->
describe_delegation_sets([], AwsConfig).
%% --------------------------------------------------------------------
%% @doc Describes delegation sets with provided aws_config and a list of AWS
%% options
%% @end
%% --------------------------------------------------------------------
-spec describe_delegation_sets(Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_delegation_set())} |
{ok, list(aws_route53_delegation_set()), string()} |
{error, term()}.
describe_delegation_sets(Options, AwsConfig) ->
do_describe_delegation_sets(Options, AwsConfig).
%% --------------------------------------------------------------------
%% @doc Describes delegation sets using default config
%% @end
%% --------------------------------------------------------------------
-spec describe_delegation_sets_all() ->
{ok, list(aws_route53_delegation_set())} |
{error, term()}.
describe_delegation_sets_all() ->
describe_all(fun describe_delegation_sets/2, [],
erlcloud_aws:default_config(), []).
%% --------------------------------------------------------------------
%% @doc Describes delegation sets using provided config
%% @end
%% --------------------------------------------------------------------
-spec describe_delegation_sets_all(AwsConfig :: aws_config()) ->
{ok, list(aws_route53_delegation_set())} |
{error, term()}.
describe_delegation_sets_all(AwsConfig) ->
describe_all(fun describe_delegation_sets/2, [], AwsConfig, []).
%% --------------------------------------------------------------------
%% @doc Describes delegation sets with provided aws_config and a list of AWS
%% options
%% @end
%% --------------------------------------------------------------------
-spec describe_delegation_sets_all(Options :: list({string(), string()}),
AwsConfig :: aws_config()) ->
{ok, list(aws_route53_delegation_set())} |
{error, term()}.
describe_delegation_sets_all(Options, AwsConfig) ->
describe_all(fun describe_delegation_sets/2, Options, AwsConfig, []).
do_describe_delegation_sets(Params, AwsConfig) ->
Path = "/" ++ ?API_VERSION ++ "/delegationset",
case route53_query(get, AwsConfig, "ListReusableDelegationSets", Path,
Params, ?API_VERSION) of
{ok, Doc} ->
Sets = xmerl_xpath:string(?DESCRIBE_DS_PATH, Doc),
Marker = erlcloud_xml:get_text(?DESCRIBE_DS_NEXT_MARKER, Doc),
make_response(Doc, ?DESCRIBE_DS_IS_TRUNCATED, Marker,
[extract_delegation_set(X) || X <- Sets]);
Error ->
Error
end.
do_describe_resource_sets(ZoneId, Params, AwsConfig) ->
Path = "/" ++ ?API_VERSION ++ "/hostedzone/" ++ ZoneId ++ "/rrset",
case route53_query(get, AwsConfig, "ListResourceRecordSets",
Path, Params, ?API_VERSION) of
{ok, Doc} ->
Sets = xmerl_xpath:string(?DESCRIBE_RS_PATH, Doc),
Marker = case {erlcloud_xml:get_text(?DESCRIBE_RS_NEXT_NAME,
Doc, undefined),
erlcloud_xml:get_text(?DESCRIBE_RS_NEXT_TYPE,
Doc, undefined),
erlcloud_xml:get_text(?DESCRIBE_RS_NEXT_ID,
Doc, undefined)
} of
{NextName, NextType, undefined} ->
{NextName, NextType};
{undefined, undefined, NextId} ->
NextId;
{NextName, NextType, NextId} ->
{NextName, NextType, NextId}
end,
make_response(Doc, ?DESCRIBE_RS_IS_TRUNCATED, Marker,
[extract_resource_set(X) || X <- Sets]);
{error, Reason} ->
{error, Reason}
end.
do_describe_zone(ZoneId, Params, Config) ->
Path = "/" ++ ?API_VERSION ++ "/hostedzone/" ++ ZoneId,
case route53_query(get, Config, "GetHostedZone",
Path, Params, ?API_VERSION) of
{ok, Doc} ->
Zone = extract_zone(hd(xmerl_xpath:string(?DESCRIBE_ZONE_PATH,
Doc))),
Extras = erlcloud_xml:decode(
[{vpcs, ?DESCRIBE_ZONE_VPCS_PATH, {optional_map, fun extract_vpcs/1}},
{delegation_set, ?DESCRIBE_ZONE_DS_PATH,
{single, fun extract_delegation_set/1}}], Doc),
{ok, Zone ++ Extras};
{error, Reason} ->
{error, Reason}
end.
do_describe_zones(Params, Config) ->
Path = "/" ++ ?API_VERSION ++ "/hostedzone",
case route53_query(get, Config, "ListHostedZones",
Path, Params, ?API_VERSION) of
{ok, Doc} ->
Zones = xmerl_xpath:string(?DESCRIBE_ZONES_PATH, Doc),
Marker = erlcloud_xml:get_text(?DESCRIBE_ZONES_NEXT_MARKER, Doc),
make_response(Doc, ?DESCRIBE_ZONES_IS_TRUNCATED, Marker,
[extract_zone(Z) || Z <- Zones]);
{error, Reason} ->
{error, Reason}
end.
make_response(Xml, Path, Marker, Result) ->
case erlcloud_xml:get_bool(Path, Xml) of
false ->
{ok, Result};
true ->
{ok, Result, Marker}
end.
extract_geolocation(Xml) ->
erlcloud_xml:decode([
{continent_code, "ContinentCode", text},
{country_code, "CountryCode", text},
{subdivision_code, "SubdivisionCode", text}
], Xml).
extract_resource_records(Xml) ->
case [erlcloud_xml:get_text("Value", X) || X <- Xml] of
[] ->
undefined;
Res ->
Res
end.
extract_alias_target(Rs) ->
erlcloud_xml:decode([
{hosted_zone_id, "HostedZoneId", text},
{dns_name, "DNSName", text},
{evaluate_target_health, "EvaluateTargetHealth",
optional_boolean}
], Rs).
extract_delegation_set(Set) ->
erlcloud_xml:decode([
{id, "Id", optional_text},
{caller_reference, "CallerReference", optional_text},
{marker, "Marker", optional_text},
{name_servers, "NameServers/NameServer", list}
], Set).
extract_resource_set(Rs) ->
erlcloud_xml:decode([
{name, "Name", text},
{type, "Type", text},
{set_identifier, "SetIdentifier", optional_text},
{weight, "Weight", optional_integer},
{region, "Region", optional_text},
{failover, "Failover", optional_text},
{health_check_id, "HealthCheckId", optional_text},
{ttl, "TTL", optional_integer},
{geo_location, "GeoLocation",
{single, fun extract_geolocation/1}},
{resource_records, "ResourceRecords/ResourceRecord",
fun extract_resource_records/1},
{alias_target, "AliasTarget",
{single, fun extract_alias_target/1}}
], Rs).
extract_zone(Xml) ->
erlcloud_xml:decode([
{zone_id, "Id", text},
{name, "Name", text},
{private, "Config/PrivateZone", optional_boolean},
{resourceRecordSetCount, "ResourceRecordSetCount",
integer},
{marker, "Marker", optional_text}
], Xml).
extract_vpcs(Xml) ->
erlcloud_xml:decode([
{vpc_id, "VPCId", text},
{vpc_region, "VPCRegion", text}
], Xml).
describe_all(Fun, Options, Config, Acc) ->
describe_all(Fun, [], Options, Config, Acc).
describe_all(Fun, Args, Options, Config, Acc) when is_list(Args) ->
case apply(Fun, Args ++ [Options] ++ [Config]) of
{ok, Res} ->
{ok, lists:foldl(fun erlang:'++'/2, [], [Res | Acc])};
{ok, Res, {Name, Type}} ->
Options1 = key_replace_or_add("name", Name, Options),
Options2 = key_replace_or_add("type", Type, Options1),
describe_all(Fun, Args, Options2, Config, [Res | Acc]);
{ok, Res, {Name, Type, Id}} ->
Options1 = key_replace_or_add("name", Name, Options),
Options2 = key_replace_or_add("type", Type, Options1),
Options3 = key_replace_or_add("identifier", Id, Options2),
describe_all(Fun, Args, Options3, Config, [Res | Acc]);
{ok, Res, Marker} ->
Options1 = key_replace_or_add("marker", Marker, Options),
describe_all(Fun, Args, Options1,
Config, [Res | Acc]);
{error, Reason} ->
{error, Reason}
end.
key_replace_or_add(Key, Value, List) ->
case lists:keymember(Key, 1, List) of
true ->
lists:keyreplace(Key, 1, List, {Key, Value});
false ->
[{Key, Value} | List]
end.
-spec route53_query(get | post, aws_config(), string(), string(),
list({string(), string()}), string()) ->
{ok, term()} | {error, term()}.
route53_query(Method, Config, Action, Path, Params, ApiVersion) ->
QParams = [{"Action", Action}, {"Version", ApiVersion} | Params],
erlcloud_aws:aws_request_xml4(Method, Config#aws_config.route53_host,
Path, QParams, "route53", Config).