public
Description: erlang node auto-discovery on EC2
Homepage: http://dukesoferl.blogspot.com/2008/02/automatic-node-discovery.html
Clone URL: git://github.com/cstar/ec2nodefinder.git
ec2nodefinder / lib / ec2nodefinder / src / awssign.erl
100644 91 lines (83 sloc) 3.325 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
-module(awssign).
-author('eric@ohmforce.com').
-include_lib("xmerl/include/xmerl.hrl").
-export([sign_and_send/5, describe_instances/5]).
 
sign_and_send(Params, Host,APIVersion, AccessKey, SecretKey)->
    SortedParams = sort([{"Timestamp", create_timestamp()},
                        {"SignatureVersion", "2"},
                        {"Version", APIVersion},
                        {"AWSAccessKeyId", AccessKey},
                        {"SignatureMethod", "HmacSHA1"}
                        |Params]),
    EncodedParams = lists:foldl(
        fun({K,V}, Acc)->
            [url_encode(K) ++ "=" ++ url_encode(V)| Acc]
        end,[], SortedParams),
    QueryString = string:join(EncodedParams, "&"),
    ToSign = "GET\n" ++ Host ++ "\n/\n" ++ QueryString,
    Signature = url_encode(
        binary_to_list(
            base64:encode(crypto:sha_mac(SecretKey, ToSign)))
        ),
    URL = "http://"++ Host ++ "/?" ++ QueryString ++ "&Signature=" ++ Signature,
    case http:request(URL) of
        {ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} -> {ok, Body};
        {ok, {{_Version, Code, ReasonPhrase}, _Headers, _Body}} -> {error, {Code, ReasonPhrase} }
    end.
 
% lifted from http://code.google.com/p/erlawys/source/browse/trunk/src/aws_util.erl
create_timestamp() -> create_timestamp(calendar:now_to_universal_time(now())).
create_timestamp({{Y, M, D}, {H, Mn, S}}) ->
to_str(Y) ++ "-" ++ to_str(M) ++ "-" ++ to_str(D) ++ "T" ++
to_str(H) ++ ":" ++ to_str(Mn)++ ":" ++ to_str(S) ++ "Z".
add_zeros(L) -> if length(L) == 1 -> [$0|L]; true -> L end.
to_str(L) -> add_zeros(integer_to_list(L)).
 
    
sort(Params)->
    lists:sort(fun({A, _}, {X, _}) -> A > X end, Params).
    
describe_instances(SecurityGroup, Host,APIVersion, AccessKey, SecretKey)->
    Params =[ {"Action", "DescribeInstances"}],
    Res = sign_and_send(Params, Host, APIVersion, AccessKey, SecretKey),
    case Res of
        {ok, XML} ->
            {R,_} = xmerl_scan:string(XML),
            [ V#xmlText.value
                || V<- xmerl_xpath:string("/DescribeInstancesResponse/reservationSet/item[ groupSet/item/groupId = \""
                        ++ SecurityGroup ++ "\"]/instancesSet/item/privateDnsName/text()", R)];
        {error, E} ->
            erlang:error ({ describe_instances_failed, E }),
            []
    end.
 
% lifted from the ever precious yaws_utils.erl
integer_to_hex(I) ->
    case catch erlang:integer_to_list(I, 16) of
        {'EXIT', _} ->
            old_integer_to_hex(I);
        Int ->
            Int
    end.
 
old_integer_to_hex(I) when I<10 ->
    integer_to_list(I);
old_integer_to_hex(I) when I<16 ->
    [I-10+$A];
old_integer_to_hex(I) when I>=16 ->
    N = trunc(I/16),
    old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16).
url_encode([H|T]) ->
    if
        H >= $a, $z >= H ->
            [H|url_encode(T)];
        H >= $A, $Z >= H ->
            [H|url_encode(T)];
        H >= $0, $9 >= H ->
            [H|url_encode(T)];
        H == $_; H == $.; H == $-; H == $/ -> % FIXME: more..
            [H|url_encode(T)];
        true ->
            case integer_to_hex(H) of
                [X, Y] ->
                    [$%, X, Y | url_encode(T)];
                [X] ->
                    [$%, $0, X | url_encode(T)]
            end
     end;
 
url_encode([]) ->
    [].