Current section

Files

Jump to
nextroll_erlcloud src erlcloud_cloudformation.erl
Raw

src/erlcloud_cloudformation.erl

-module (erlcloud_cloudformation).
-include("erlcloud.hrl").
-include("erlcloud_aws.hrl").
-define(API_VERSION, "2010-05-15").
-type access_key_id() :: string().
-type secret_access_key() :: string().
-type params() :: proplists:proplist().
-type cloudformation_list() :: [proplists:proplist()].
-type error_reason() :: metadata_not_available | container_credentials_unavailable | erlcloud_aws:httpc_result_error().
%% Library initialization
-export([
configure/2,
new/2
]).
%% Cloud Formation API Functions
-export ([
list_stacks_all/1,
list_stacks_all/2,
list_stacks/2,
list_stacks/1,
list_stack_resources_all/2,
list_stack_resources_all/3,
list_stack_resources/2,
list_stack_resources/1,
describe_stack_resources/2,
describe_stack_resources/3,
describe_stack_resource/3,
describe_stack_resource/4,
describe_stacks_all/1,
describe_stacks_all/2,
describe_stacks/2,
describe_stacks/1,
get_stack_policy/2,
get_stack_policy/3,
get_template/2,
get_template/1,
get_template_summary/2,
get_template_summary/3,
describe_account_limits_all/0,
describe_account_limits_all/1,
describe_account_limits/2,
describe_account_limits/1,
describe_stack_events_all/1,
describe_stack_events_all/2,
describe_stack_events/2,
describe_stack_events/1]).
%%==============================================================================
%% Library initialization
%%==============================================================================
-spec configure(access_key_id(), secret_access_key()) -> ok.
configure(AccessKeyID, SecretAccessKey) ->
erlcloud_config:configure(AccessKeyID, SecretAccessKey, fun new/2).
-spec new(access_key_id(), secret_access_key()) -> #aws_config{}.
new(AccessKeyID, SecretAccessKey) ->
#aws_config{
access_key_id = AccessKeyID,
secret_access_key = SecretAccessKey
}.
%%==============================================================================
%% Cloud Formation API Functions
%%==============================================================================
-spec list_stacks_all(params()) -> {ok, cloudformation_list()} | {error, error_reason()}.
list_stacks_all(Params) ->
list_stacks_all(Params, default_config()).
-spec list_stacks_all(params(), aws_config()) -> {ok, cloudformation_list()} | {error, error_reason()}.
list_stacks_all(Params, Config = #aws_config{}) ->
list_all(fun list_stacks/2, Params, Config, []).
-spec list_stacks(params()) -> {ok, cloudformation_list(), NextToken :: undefined | string()}
| {error, error_reason()}.
list_stacks(Params) ->
list_stacks(Params, default_config()).
-spec list_stacks(params(), aws_config()) -> {ok, cloudformation_list(), NextToken :: undefined | string()}
| {error, error_reason()}.
list_stacks(Params, Config = #aws_config{}) ->
ExtraParams= lists:map(fun(T) ->
case T of
{stack_status_filter, N, Filter} ->
{"StackStatusFilter.member." ++ integer_to_list(N), Filter};
_ -> convert_param(T)
end
end, Params),
case cloudformation_request(Config, "ListStacks", ExtraParams) of
{ok, XmlNode} ->
NextToken = erlcloud_xml:get_text(
"/ListStacksResponse/ListStacksResult/NextToken",
XmlNode,
undefined),
StackSummaries = extract_stack_summaries(xmerl_xpath:string(
"/ListStacksResponse/ListStacksResult",
XmlNode)),
{ok, StackSummaries, NextToken};
{error, Error} ->
{error, Error}
end.
-spec list_stack_resources_all(params(), string()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
list_stack_resources_all(Params, StackName) ->
list_stack_resources_all(Params, StackName, default_config()).
-spec list_stack_resources_all(params(), string(), aws_config()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
list_stack_resources_all(Params, StackName, Config = #aws_config{}) ->
FullParams = [{"StackName", StackName}
| lists:map(
fun(T) ->
convert_param(T)
end, Params)],
list_all(fun list_stack_resources/2, FullParams, Config, []).
-spec list_stack_resources(params()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
list_stack_resources(Params) ->
list_stack_resources(Params, default_config()).
-spec list_stack_resources(params(), aws_config()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
list_stack_resources(Params, Config = #aws_config{}) ->
case cloudformation_request(Config, "ListStackResources", Params) of
{ok, XmlNode} ->
NextToken = erlcloud_xml:get_text(
"/ListStackResourcesResponse/"
"ListStackResourcesResult/NextToken",
XmlNode,
undefined),
StackResources = extract_list_stack_resources(xmerl_xpath:string(
"/ListStackResourcesResponse/ListStackResourcesResult",
XmlNode)),
{ok, StackResources, NextToken};
{error, Error} ->
{error, Error}
end.
-spec describe_stack_resources(params(), string()) ->
cloudformation_list() | {error, error_reason()}.
describe_stack_resources(Params, StackName) ->
describe_stack_resources(Params, StackName, default_config()).
-spec describe_stack_resources(params(), string(), aws_config()) ->
cloudformation_list() | {error, error_reason()}.
describe_stack_resources(Params, StackName, Config = #aws_config{}) ->
FullParams = [{"StackName", StackName}
| lists:map(
fun(T) ->
convert_param(T)
end, Params)],
case cloudformation_request(Config, "DescribeStackResources", FullParams) of
{ok, XmlNode} ->
extract_stack_resources_members(xmerl_xpath:string(
"/DescribeStackResourcesResponse",
XmlNode));
{error, Error} ->
{error, Error}
end.
-spec describe_stack_resource(params(), string(), string()) ->
cloudformation_list() | {error, error_reason()}.
describe_stack_resource(Params, StackName, LogicalResourceId) ->
describe_stack_resource(Params, StackName, LogicalResourceId,
default_config()).
-spec describe_stack_resource(params(), string(), string(), aws_config()) ->
cloudformation_list() | {error, error_reason()}.
describe_stack_resource(Params, StackName, LogicalResourceId,
Config = #aws_config{}) ->
FullParams = [{"StackName", StackName},
{"LogicalResourceId", LogicalResourceId}
| lists:map(
fun(T) ->
convert_param(T)
end,Params)],
case cloudformation_request(Config, "DescribeStackResource", FullParams) of
{ok, XmlNode} ->
extract_stack_details(xmerl_xpath:string(
"/DescribeStackResourceResponse",
XmlNode));
{error, Error} ->
{error, Error}
end.
-spec describe_stacks_all(params()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
describe_stacks_all(Params) ->
describe_stacks_all(Params, default_config()).
-spec describe_stacks_all(params(), aws_config()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
describe_stacks_all(Params, Config = #aws_config{}) ->
RequestParams = lists:map(fun(T) -> convert_param(T) end, Params),
list_all(fun describe_stacks/2, RequestParams, Config, []).
-spec describe_stacks(params()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
describe_stacks(Params) ->
describe_stacks(Params, default_config()).
-spec describe_stacks(params(), aws_config()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
describe_stacks(Params, Config = #aws_config{}) ->
case cloudformation_request(Config, "DescribeStacks", Params) of
{ok, XmlNode} ->
NextToken = erlcloud_xml:get_text(
"/DescribeStacksResponse/DescribeStacksResult/NextToken",
XmlNode,
undefined),
DescribedStacks = extract_described_stacks_result(
xmerl_xpath:string(
"/DescribeStacksResponse",
XmlNode)
),
{ok, DescribedStacks, NextToken};
{error, Error} ->
{error, Error}
end.
-spec get_stack_policy(params(), string()) ->
cloudformation_list() | {error, error_reason()}.
get_stack_policy(Params, StackName) ->
get_stack_policy(Params, StackName, default_config()).
-spec get_stack_policy(params(), string(), aws_config()) ->
cloudformation_list() | {error, error_reason()}.
get_stack_policy(Params, StackName, Config = #aws_config{}) ->
FullParams = [{"StackName", StackName}
| lists:map(
fun(T) ->
convert_param(T)
end, Params)],
case cloudformation_request(Config, "GetStackPolicy", FullParams) of
{ok, XmlNode} ->
extract_stack_policy_body(xmerl_xpath:string(
"/GetStackPolicyResponse",
XmlNode));
{error, Error} ->
{error, Error}
end.
-spec describe_stack_events_all(params()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
describe_stack_events_all(Params) ->
describe_stack_events_all(Params, default_config()).
-spec describe_stack_events_all(params(), aws_config()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
describe_stack_events_all(Params, Config = #aws_config{}) ->
RequestParams = lists:map(
fun(T) ->
convert_param(T)
end, Params),
list_all(fun describe_stack_events/2, RequestParams, Config, []).
-spec describe_stack_events(params()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
describe_stack_events(Params) ->
describe_stack_events(Params, default_config()).
-spec describe_stack_events(params(), aws_config()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
describe_stack_events(Params, Config = #aws_config{}) ->
case cloudformation_request(Config, "DescribeStackEvents", Params) of
{ok, XmlNode} ->
NextToken = erlcloud_xml:get_text(
"/DescribeStackEventsResponse/DescribeStackEventsResult"
"/NextToken",
XmlNode,
undefined),
StackEvents = extract_described_stack_events_result(
xmerl_xpath:string("/DescribeStackEventsResponse", XmlNode)),
{ok, StackEvents, NextToken};
{error, Error} ->
{error, Error}
end.
-spec get_template(string()) ->
cloudformation_list() | {error, error_reason()}.
get_template(StackName) ->
get_template(StackName, default_config()).
-spec get_template(string(), aws_config()) ->
cloudformation_list() | {error, error_reason()}.
get_template(StackName, Config = #aws_config{}) ->
case cloudformation_request(Config, "GetTemplate",
[{"StackName", StackName}]) of
{ok, XmlNode} ->
extract_template_response(XmlNode);
{error, Error} ->
{error, Error}
end.
-spec get_template_summary(params(), string()) ->
cloudformation_list() | {error, error_reason()}.
get_template_summary(Params, StackName) ->
get_template_summary(Params, StackName, default_config()).
-spec get_template_summary(params(), string(), aws_config()) ->
cloudformation_list() | {error, error_reason()}.
get_template_summary(Params, StackName, Config = #aws_config{}) ->
FullParams = [{"StackName", StackName}
| lists:map(
fun(T) ->
convert_param(T)
end, Params)],
case cloudformation_request(Config, "GetTemplateSummary", FullParams) of
{ok, XmlNodes} ->
extract_template_summary_response(XmlNodes);
{error, Error} ->
{error, Error}
end.
-spec describe_account_limits_all() ->
{ok, cloudformation_list()} | {error, error_reason()}.
describe_account_limits_all() ->
describe_account_limits_all(default_config()).
-spec describe_account_limits_all(aws_config()) ->
{ok, cloudformation_list()} | {error, error_reason()}.
describe_account_limits_all(Config = #aws_config{}) ->
list_all(fun describe_account_limits/2, [], Config, []).
-spec describe_account_limits(params()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
describe_account_limits(Params) ->
describe_account_limits(Params, default_config()).
-spec describe_account_limits(params(), aws_config()) ->
{ok, cloudformation_list(), NextToken :: undefined | string()} | {error, error_reason()}.
describe_account_limits(Params, Config = #aws_config{}) ->
RequestParams = lists:map(fun(T) -> convert_param(T) end, Params),
case cloudformation_request(Config, "DescribeAccountLimits",
RequestParams) of
{ok, XmlNodes} ->
NextToken = erlcloud_xml:get_text(
"/DescribeAccountLimitsResponse/DescribeAccountLimitsResult/"
"NextToken",
XmlNodes,
undefined),
AccountLimits = extract_accout_limits_response(xmerl_xpath:string(
"/DescribeAccountLimitsResponse",
XmlNodes)),
{ok, AccountLimits, NextToken};
{error, Error} ->
{error, Error}
end.
%%==============================================================================
%% Internal functions
%%==============================================================================
convert_param({Key, Value}) ->
case Key of
stack_name -> {"StackName", Value};
stack_id -> {"StackName", Value};
logical_resource_id -> {"LogicalResourceId", Value};
next_token -> {"NextToken", Value}
end.
default_config() ->
erlcloud_aws:default_config().
cloudformation_request(Config = #aws_config{}, Action, ExtraParams) ->
QParams = [
{"Action", Action},
{"Version", ?API_VERSION}
| ExtraParams],
erlcloud_aws:aws_request_xml4(post, Config#aws_config.cloudformation_host,
"/", QParams, "cloudformation", Config).
list_all(Fun, Params, Config, Acc) ->
case Fun(Params, Config) of
{ok, Data, NextToken} ->
case NextToken of
undefined ->
lists:foldl(fun erlang:'++'/2, [], [Data | Acc]);
_ ->
list_all(Fun, [{next_token, NextToken} | Params],
Config, [Data | Acc])
end;
{error, Reason} ->
{error, Reason}
end.
extract_stack_summaries(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([{summaries, "StackSummaries",
{map, fun extract_stacks/1}}], T) end, XmlNodes).
extract_stacks(XmlNode) ->
erlcloud_xml:decode([
{member, "member", {map, fun extract_stack/1}}
], XmlNode).
extract_stack(XmlNode) ->
erlcloud_xml:decode([
{stack_id, "StackId", optional_text},
{stack_status, "StackStatus", optional_text},
{stack_name, "StackName", optional_text},
{creation_time, "CreationTime", optional_text},
{template_description, "TemplateDescription", optional_text},
{resource_types, "ResourceTypes", list}], XmlNode).
extract_list_stack_resources(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([
{summaries, "StackResourceSummaries",
{map, fun extract_list_resources/1}}
], T) end, XmlNodes).
extract_list_resources(XmlNode) ->
erlcloud_xml:decode([
{member, "member", {map, fun extract_list_resource/1}}
], XmlNode).
extract_list_resource(XmlNode) ->
erlcloud_xml:decode([
{resource_status, "ResourceStatus", optional_text},
{logical_resource_id, "LogicalResourceId", optional_text},
{last_updated_timestamp, "LastUpdatedTimestamp", optional_text},
{physical_resource_id, "PhysicalResourceId", optional_text},
{resource_type, "ResourceType", optional_text}
], XmlNode).
extract_stack_resources_members(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([
{resources, "DescribeStackResourcesResult/StackResources",
{map, fun extract_member_resources/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], T) end, XmlNodes).
extract_member_resources(XmlNode) ->
erlcloud_xml:decode([{member, "member",
{map, fun extract_resource/1}}], XmlNode).
extract_resource(XmlNode) ->
erlcloud_xml:decode([
{stack_id, "StackId", optional_text},
{stack_name, "StackName", optional_text},
{logical_resource_id, "LogicalResourceId", optional_text},
{physical_resource_id, "PhysicalResourceId", optional_text},
{resource_type, "ResourceType", optional_text},
{timestamp, "Timestamp", optional_text},
{resource_status, "ResourceStatus", optional_text}
], XmlNode).
extract_stack_details(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode(
[{resources, "DescribeStackResourceResult/StackResourceDetail",
{map, fun extract_resource/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], T) end, XmlNodes).
extract_described_stacks_result(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([
{stacks, "DescribeStacksResult/Stacks",
{map, fun extract_described_stacks/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], T) end, XmlNodes).
extract_described_stacks(XmlNode) ->
erlcloud_xml:decode([{member, "member",
{map, fun extract_described_stack/1}}], XmlNode).
extract_described_stack(XmlNode) ->
erlcloud_xml:decode([
{stack_name, "StackName", optional_text},
{stack_id, "StackId", optional_text},
{creation_time, "CreationTime", optional_text},
{stack_status, "StackStatus", optional_text},
{disable_rollback, "DisableRollback", optional_text},
{parameters, "Parameters", {map, fun extract_parameters/1}},
{outputs, "Outputs", {map, fun extract_resource_outputs/1}}
], XmlNode).
extract_parameters(XmlNode) ->
erlcloud_xml:decode([
{member, "member", {map, fun extract_parameter/1}}
], XmlNode).
extract_parameter(XmlNode) ->
erlcloud_xml:decode([
{parameter_key, "ParameterKey", optional_text},
{parameter_value, "ParameterValue", optional_text}
], XmlNode).
extract_resource_outputs(XmlNode) ->
erlcloud_xml:decode([
{member, "member", {map, fun extract_resource_output/1}}
], XmlNode).
extract_resource_output(XmlNode) ->
erlcloud_xml:decode([
{description, "Description", optional_text},
{output_key, "OutputKey", optional_text},
{output_value, "OutputValue", optional_text}
], XmlNode).
extract_stack_policy_body(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([
{stack_policy_body, "GetStackPolicyResult/StackPolicyBody/",
optional_text},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], T) end, XmlNodes).
extract_described_stack_events_result(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([
{stack_events, "DescribeStackEventsResult/StackEvents",
{map, fun extract_stack_event_member/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], T) end, XmlNodes).
extract_stack_event_member(XmlNode) ->
erlcloud_xml:decode([
{member, "member", {map, fun extract_stack_event/1}}
], XmlNode).
extract_stack_event(XmlNode) ->
erlcloud_xml:decode([
{event_id, "EventId", optional_text},
{stack_id, "StackId", optional_text},
{stack_name, "StackName", optional_text},
{logical_resource_id, "LogicalResourceId", optional_text},
{physical_resource_id, "PhysicalResourceId", optional_text},
{resource_type, "ResourceType", optional_text},
{timestamp, "TimeStamp", optional_text},
{resource_status, "ResourceStatus", optional_text},
{resource_properties, "ResourceProperties", optional_text}
], XmlNode).
extract_template_response(XmlNodes) ->
erlcloud_xml:decode([
{template_result, "GetTemplateResult",
{map, fun extract_template_result/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], XmlNodes).
extract_template_meta_body(XmlNode) ->
erlcloud_xml:decode([{request_id, "RequestId", optional_text}], XmlNode).
extract_template_result(XmlNode) ->
erlcloud_xml:decode([{template_body, "TemplateBody", optional_text}],
XmlNode).
extract_template_summary_response(XmlNodes) ->
erlcloud_xml:decode([
{template_summary_result, "GetTemplateSummaryResult",
{map, fun extract_template_summary_result/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], XmlNodes).
extract_template_summary_result(XmlNode) ->
erlcloud_xml:decode([
{description, "Description", optional_text},
{parameters, "Parameters",
{map, fun extract_template_parameters/1}},
{metadata, "Metadata", optional_text},
{version, "Version", optional_text}
], XmlNode).
extract_template_parameters(XmlNode) ->
erlcloud_xml:decode([
{member, "member",
{map, fun extract_template_parameter/1}}
], XmlNode).
extract_template_parameter(XmlNode) ->
erlcloud_xml:decode([
{no_echo, "NoEcho", optional_text},
{parameter_key, "ParameterKey", optional_text},
{description, "Description", optional_text},
{parameter_type, "ParameterType", optional_text}
], XmlNode).
extract_accout_limits_response(XmlNodes) ->
lists:map(fun(T) -> erlcloud_xml:decode([
{describe_account_limits_result, "DescribeAccountLimitsResult",
{map, fun extract_account_limits_result/1}},
{response_meta, "ResponseMetadata",
{map, fun extract_template_meta_body/1}}
], T) end, XmlNodes).
extract_account_limits_result(XmlNode) ->
erlcloud_xml:decode([
{account_limits, "AccountLimits",
{map, fun extract_account_limits/1}}
], XmlNode).
extract_account_limits(XmlNode) ->
erlcloud_xml:decode([
{member, "member",
{map, fun extract_account_limit/1}}
], XmlNode).
extract_account_limit(XmlNode) ->
erlcloud_xml:decode([
{name, "Name", optional_text},
{value, "Value", optional_text}
], XmlNode).