Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

EQC tests and a simple eunit test to run them. #11

Merged
merged 7 commits into from

2 participants

@massung

No description provided.

@jtuple

Lager is now a dependency for riak_core, so your test no longer works
against Riak master/1.1. You'll need to add lager to the list of applications
you spin up. Or, even use the built-in dependency management feature of Riak
so that you don't need to list apps explicitly.

Adding lager:

--- a/test/eqc_routes.erl
+++ b/test/eqc_routes.erl
@@ -36,6 +36,7 @@ prop_routes () ->
                                                    riak_sysmon,
                                                    webmachine,
                                                    os_mon,
+                                                   lager,
                                                    riak_core,
                                                    riak_control]],

Using built-in dependency management:

--- a/test/eqc_routes.erl
+++ b/test/eqc_routes.erl
@@ -30,14 +30,9 @@ prop_routes () ->
                 setup(Auth),

                 %% start applications
-                [application:start(App) || App <- [sasl,
-                                                   crypto,
-                                                   inets,
-                                                   riak_sysmon,
-                                                   webmachine,
-                                                   os_mon,
-                                                   riak_core,
-                                                   riak_control]],
+                riak_core_util:start_app_deps(riak_core),
+                riak_core_util:start_app_deps(riak_control),
+                application:start(riak_control),

You currently have the SSL certificates hard-coded to a path in your home
directory. I imagine there are many ways to change this to be portable, but
I think the easiest would be to use the priv_dir support provided by Erlang.
In my testing, I copied riak/rel/files/*.pem to riak_control/priv and
modified the code as follows:

--- a/test/eqc_routes.erl
+++ b/test/eqc_routes.erl
@@ -197,8 +192,9 @@ setup (Auth) ->
     %% set env values for riak core
     set(http, [{"127.0.0.1",18098}]),
     set(https, [{"127.0.0.1",18069}]),
-    set(ssl, [{certfile, "/Users/jeff/Projects/b/riak/rel/files/cert.pem"},
-              {keyfile, "/Users/jeff/Projects/b/riak/rel/files/key.pem"}
+    CertPath = code:priv_dir(riak_control),
+    set(ssl, [{certfile, CertPath ++ "/cert.pem"},
+              {keyfile, CertPath ++ "/key.pem"}
              ]),

After these minor changes, the EQC tests passed several runs. Even when I
temporarily changed EQC to generate 1000 tests as well as modified the
command generator to generate 10x larger command sequences.

So, just need to fix the lager and SSL cert issues.

@massung massung Fixed startup applications (per Joe's suggestion) and made the cert f…
…iles local to the test instead of hard-coded paths.
fd35afa
@massung

Updated based on Joe's suggestions. Tests run an pass still.

@massung massung merged commit deaac86 into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 13, 2012
  1. @massung

    Initial eqc test.

    massung authored
Commits on Jan 19, 2012
  1. @massung
Commits on Jan 20, 2012
  1. @massung
Commits on Jan 23, 2012
  1. @massung
Commits on Jan 25, 2012
  1. @massung
  2. @massung

    Removed old eqc test.

    massung authored
Commits on Feb 28, 2012
  1. @massung

    Fixed startup applications (per Joe's suggestion) and made the cert f…

    massung authored
    …iles local to the test instead of hard-coded paths.
This page is out of date. Refresh to see the latest.
View
28 src/riak_control_security.erl
@@ -87,35 +87,35 @@ https_redirect (RD,Ctx) ->
%% - `none' :: No authentication.
%%
enforce_auth(RD, Ctx) ->
- case wrq:get_req_header("authorization", RD) of
- "Basic "++Base64 ->
- enforce_basic_auth(RD, Ctx, Base64);
- _ ->
- {?ADMIN_AUTH_HEAD, RD, Ctx}
+ case app_helper:get_env(riak_control,auth,none) of
+ none ->
+ {true, RD, Ctx};
+ Auth ->
+ case wrq:get_req_header("authorization", RD) of
+ "Basic "++Base64 ->
+ enforce_basic_auth(RD, Ctx, Base64, Auth);
+ _ ->
+ {?ADMIN_AUTH_HEAD, RD, Ctx}
+ end
end.
-enforce_basic_auth(RD, Ctx, Base64) ->
+enforce_basic_auth(RD, Ctx, Base64, Auth) ->
Str = base64:mime_decode_to_string(Base64),
case string:tokens(Str, ":") of
[User, Pass] ->
- enforce_user_pass(RD, Ctx, User, Pass);
+ enforce_user_pass(RD, Ctx, User, Pass, Auth);
_ ->
{?ADMIN_AUTH_HEAD, RD, Ctx}
end.
-enforce_user_pass(RD, Ctx, User, Pass) ->
- case valid_userpass(User, Pass) of
+enforce_user_pass(RD, Ctx, User, Pass, Auth) ->
+ case valid_userpass(User, Pass, Auth) of
true ->
{true, RD, Ctx};
false ->
{?ADMIN_AUTH_HEAD, RD, Ctx}
end.
-%% validate the username and password
-valid_userpass(User, Pass) ->
- Auth=app_helper:get_env(riak_control, auth),
- valid_userpass(User, Pass, Auth).
-
%% validate the username and password with the given auth style
valid_userpass(_User, _Pass, none) ->
true;
View
24 src/riak_control_session.erl
@@ -21,6 +21,10 @@
-module(riak_control_session).
-behavior(gen_server).
+-ifdef (TEST).
+-include_lib("eunit/include/eunit.hrl").
+-endif.
+
%% API
-export([start_link/0,
get_version/0,
@@ -321,3 +325,23 @@ get_vnode_status (Service,Ring,Index) ->
[] -> {Service,undefined}
end.
+
+%% ---------------------------------------------------------------------------
+%% EUNIT tests
+
+-ifdef(TEST).
+
+%% we want to see quickcheck output
+-define(QC_OUT(P),
+ eqc:on_output(fun(Str, Args) ->
+ io:format(user, Str, Args)
+ end,
+ P)).
+
+quickcheck_test_ () ->
+ {timeout, 120,
+ fun() ->
+ ?assert(eqc:quickcheck(?QC_OUT(eqc_routes:prop_routes())))
+ end}.
+
+-endif.
View
17 test/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvTCCAiYCCQDgxT3HogRJ/TANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMC
+VVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEf
+MB0GA1UEChMWQmFzaG8gVGVjaG5vbG9naWVzIEluYzENMAsGA1UECxMEUmlhazEY
+MBYGA1UEAxMPSmVmZnJleSBNYXNzdW5nMR0wGwYJKoZIhvcNAQkBFg5qZWZmQGJh
+c2hvLmNvbTAeFw0xMTEwMzExNjQ3NTNaFw0yMTEwMjgxNjQ3NTNaMIGiMQswCQYD
+VQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czESMBAGA1UEBxMJQ2FtYnJp
+ZGdlMR8wHQYDVQQKExZCYXNobyBUZWNobm9sb2dpZXMgSW5jMQ0wCwYDVQQLEwRS
+aWFrMRgwFgYDVQQDEw9KZWZmcmV5IE1hc3N1bmcxHTAbBgkqhkiG9w0BCQEWDmpl
+ZmZAYmFzaG8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkys/CB8Ce
+fO19JsFiL2K5pODMWFmXxfQgvARB2rIvoJ4R4mNKI639xRbR+gCPreJvZRw8trgD
+8sARFv8J1SlqqYRDN8zfMlvolXh6Atujou/LwjUpTA3pMe9lZrWU1+JZOMAk79lz
+O/1etfR12By0SqNfDgUMIIZST7i3Fw2IkwIDAQABMA0GCSqGSIb3DQEBBQUAA4GB
+ABTOXMBYoEn0biwqaDcZzInZvZHupFkmkOABumgJ5xVDQ/9LIzy9mfg0Ko+JERlM
+0w0dliu2sfFoXLps9EohjIzrU1B3CwSNvNqPmcj9V4k0iXrsvDfbG9eJ9nYaUY0Y
+L5I9/KAOIf3fEmnFbjtmyiLVhrM6kBB3fvoAVQfwL6cZ
+-----END CERTIFICATE-----
View
243 test/eqc_routes.erl
@@ -0,0 +1,243 @@
+-module(eqc_routes).
+-compile(export_all).
+
+-ifdef(EQC).
+
+%% EQC headers
+-include_lib("eqc/include/eqc.hrl").
+-include_lib("eqc/include/eqc_statem.hrl").
+
+%% command generators
+-define(CLUSTER_LIST,{call,?MODULE,cluster_list,[]}).
+-define(PARTITION_LIST,{call,?MODULE,partition_list,[]}).
+-define(HTTP_CLUSTER_LIST(U),{call,?MODULE,http_cluster_list,[U]}).
+-define(HTTP_PARTITION_LIST(U,F),{call,?MODULE,http_partition_list,[U,F]}).
+
+
+%% ---------------------------------------------------------------------------
+%% statem state
+
+-record(state,{auth,ring_size}).
+
+
+%% ---------------------------------------------------------------------------
+%% properties
+
+prop_routes () ->
+ ?FORALL(Auth,g_auth(),
+ collect(Auth,
+ begin
+ setup(Auth),
+
+ %% start application dependencies
+ riak_core_util:start_app_deps(riak_core),
+ riak_core_util:start_app_deps(riak_control),
+
+ %% can now start riak control
+ application:start(riak_control),
+
+ %% ensure riak_control is up and running
+ riak_core:wait_for_application(riak_control),
+ {ok,Size}=application:get_env(riak_core,ring_creation_size),
+
+ %% now run the test with these auth settings
+ ?ALWAYS(10,
+ ?FORALL(Cmds,commands(?MODULE,#state{auth=Auth,
+ ring_size=Size
+ }),
+ begin
+ {H,S,Res}=run_commands(?MODULE,Cmds),
+
+ %% results
+ ?WHENFAIL(
+ io:format("H: ~p\nS: ~p\nR: ~p\n",
+ [H,S,Res]),
+ Res==ok)
+ end))
+ end)).
+
+
+%% ---------------------------------------------------------------------------
+%% statem behavior
+
+initial_state () ->
+ #state{}.
+
+command (_State) ->
+ oneof([?CLUSTER_LIST,
+ ?PARTITION_LIST,
+ ?LET(User,g_userpass(),
+ oneof([?HTTP_CLUSTER_LIST(User),
+ ?LET(Filter,g_qs(),
+ ?HTTP_PARTITION_LIST(User,Filter))
+ ]))
+ ]).
+
+precondition (_State,_Gen) ->
+ true.
+
+postcondition (State,{call,_,http_cluster_list,[User]},Res) ->
+ http_validate(State,Res,User) andalso cluster_validate(State,Res);
+postcondition (State,{call,_,http_partition_list,[User,Filter]},Res) ->
+ http_validate(State,Res,User) andalso filter_validate(State,Res,Filter);
+postcondition (_State,_Gen,_Res) ->
+ true.
+
+next_state (State,_V,_Gen) ->
+ State.
+
+
+%% ---------------------------------------------------------------------------
+%% negative testing common post-conditions
+
+http_validate (#state{auth=userlist},{Code,_,_},{user,pass}) ->
+ Code < 300;
+http_validate (#state{auth=userlist},{Code,_,_},_) ->
+ Code >= 300;
+http_validate (#state{auth=none},{Code,_,_},_) ->
+ Code < 300.
+
+
+cluster_validate (_State,{Code,"",_Url}) ->
+ not (Code < 300);
+cluster_validate (_State,{_Code,Body,_Url}) ->
+ [{struct,Node}]=mochijson2:decode(Body),
+ {_,Name}=lists:keyfind(<<"name">>,1,Node),
+ {_,Me}=lists:keyfind(<<"me">>,1,Node),
+ {_,Reachable}=lists:keyfind(<<"reachable">>,1,Node),
+ binary_to_atom(Name,utf8) == node() andalso Me andalso Reachable.
+
+
+filter_validate (_State,{Code,"",_Url},_Filter) ->
+ not (Code < 300);
+filter_validate (#state{ring_size=Size},{_Code,Body,_Url},Filter) ->
+ {struct,Json}=mochijson2:decode(Body),
+ {_,Pages}=lists:keyfind(<<"pages">>,1,Json),
+ {_,Page}=lists:keyfind(<<"page">>,1,Json),
+ {_,Contents}=lists:keyfind(<<"contents">>,1,Json),
+ filter_validate_n(Size,Pages,Page,Contents,Filter) andalso
+ %% TODO: more validation here
+ true.
+
+
+filter_validate_n (Size,Pages,_Page,Contents,Filter) ->
+ ContentSize=erlang:length(Contents),
+ N=case lists:keyfind(n,1,Filter) of
+ false -> Size div Pages;
+ {_,X} -> X
+ end,
+ %% less than is okay for now, we might have returned the last page
+ ContentSize =< max(N,16).
+
+
+%% ---------------------------------------------------------------------------
+%% eqc generators
+
+%% valid authorization modes for app.config
+g_auth () ->
+ oneof([userlist,none]).
+
+%% username and password pairs (none = don't pass user/pass)
+g_userpass () ->
+ oneof([{user,pass},
+ {user,bad_password},
+ {bad_user,pass},
+ {bad_user,bad_password},
+ none % not passing auth data
+ ]).
+
+%% query string generator
+g_qs () ->
+ ?LET(Filter,g_filter(),
+ ?LET(Page,g_page(),
+ Filter ++ Page)).
+
+%% valid and invalid query string filters for ring page
+g_filter () ->
+ oneof([[{filter,node}],
+ [{filter,node},{q,g_node()}],
+ [{q,g_node()}],
+ [{filter,handoffs}],
+ [{filter,fallbacks}],
+ []
+ ]).
+
+%% pagination data for ring page
+g_page () ->
+ oneof([[{n,int()},{p,int()}],
+ [{n,int()}],
+ [{p,int()}],
+ []
+ ]).
+
+%% this node or a dummy node
+g_node () ->
+ oneof([node(),'dummy@nohost']).
+
+
+%% ---------------------------------------------------------------------------
+%% private
+
+f (Fmt,Args) ->
+ lists:flatten(io_lib:format(Fmt,Args)).
+
+set (K,V) ->
+ application:set_env(riak_core,K,V).
+
+setup (Auth) ->
+ application:load(riak_core),
+ application:load(riak_control),
+
+ %% hide sasl output
+ application:set_env(sasl,sasl_error_logger,false),
+
+ %% local the pem files in the test folder
+ PrivDir=code:priv_dir(riak_control),
+ TestDir=filename:join([PrivDir,"..","test"]),
+ CertFile=filename:join([TestDir,"cert.pem"]),
+ KeyFile=filename:join([TestDir,"key.pem"]),
+
+ %% set env values for riak core
+ set(http, [{"127.0.0.1",18098}]),
+ set(https, [{"127.0.0.1",18069}]),
+ set(ssl, [{certfile, CertFile},
+ {keyfile, KeyFile}
+ ]),
+
+ %% finally, enable riak control
+ application:set_env(riak_control,userlist,[{"user","pass"}]),
+ application:set_env(riak_control,enabled,true),
+ application:set_env(riak_control,admin,true),
+ application:set_env(riak_control,auth,Auth).
+
+cluster_list () ->
+ {ok,_V,Nodes}=riak_control_session:get_nodes(),
+ Nodes.
+
+partition_list () ->
+ {ok,_V,Ring}=riak_control_session:get_ring(),
+ Ring.
+
+http_cluster_list (User) ->
+ http_request(User,"/admin/cluster/list",[]).
+
+http_partition_list (User,Filter) ->
+ http_request(User,"/admin/ring/partitions",Filter).
+
+http_request (User,Route,Query) ->
+ Url=request_url(User,Route,Query),
+ case httpc:request(get,{Url,[]},[],[]) of
+ {ok,{{_HTTP,Code,_OK},_Headers,Body}} -> {Code,Body,Url};
+ {ok,{{_HTTP,Code,_OK},Body}} -> {Code,Body,Url}
+ end.
+
+request_url (User,Route,Query) ->
+ f("https://~slocalhost:18069~s?~s",[auth_user(User),Route,qs(Query)]).
+
+auth_user ({User,Pass}) -> f("~s:~s@",[User,Pass]);
+auth_user (_) -> "".
+
+qs ([{K,V}|QS]) -> f("~s=~w&~s", [K,V,qs(QS)]);
+qs ([]) -> "".
+
+-endif. % EQC
View
15 test/key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCkys/CB8CefO19JsFiL2K5pODMWFmXxfQgvARB2rIvoJ4R4mNK
+I639xRbR+gCPreJvZRw8trgD8sARFv8J1SlqqYRDN8zfMlvolXh6Atujou/LwjUp
+TA3pMe9lZrWU1+JZOMAk79lzO/1etfR12By0SqNfDgUMIIZST7i3Fw2IkwIDAQAB
+AoGANAW2eolZ/G5xxo2ChQ1yfCqZsMi/V9NtExxnt6Zjk/d/jyPJtnD3D2K1ponm
+vXTmQ8ZGmMAR7WUnzv1Ue/UoAntcyXwKAm+T+2IUJiir/qzYKLSn8FJ3wA+OWYKs
+1nSryi54IuNenKUslxMPDPk/0bM6nZS2AvbNPYhX7a8evXkCQQDNdsvDO3Ofn0pJ
++3bMLH5Ch/adrJ0TfF1H8n9pxiuq813ppffXsyzv3haTbGHOtEM5tILYbHmO16h1
+vY+hhoHHAkEAzVMVRaDefjp1qKfoyRm9ySa3GgH71t1dvm1jGTRxNk9M8pekLDz+
+GWyTzffM2/+8Xz4RFzLjqAoAjzBGMeAC1QJAANiycjV2fnvbhH6CuMieJIwG2hNx
++jiS8c7v83GbkHK8OlAyuzLDxqE1mpnhtUZM2JoDx/x6a7o7uXB0fQfe1QJBAKxi
+d/aYhJS4IjaymqfUm9m5TntgdP9FpcIOdugfdnmhhLochLK7lp7j4QhJZ07B3Hae
+Vp0Clc5sb2HIpvaS2+0CQEV5NxPjavmlCQksQvU2OAQvTW3Sm9lahTl4XvdVxfj0
+G9sZ2erg7MIo2LF4V6FM6Hbfoj/FhAMOXlXUoUGs1uI=
+-----END RSA PRIVATE KEY-----
Something went wrong with that request. Please try again.