summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lehnardt <jan@apache.org>2009-10-11 05:49:19 +0000
committerJan Lehnardt <jan@apache.org>2009-10-11 05:49:19 +0000
commite3610fa481b5c506ca0b69f49a6f350fce8b3399 (patch)
treeb0050828126b4bf2dda4eb1c977630787d7b0f3a
parente3507c9e1bb57986ccffc9324d99bec4d0e4cc58 (diff)
add create_target:true option to _replicate that creates the target database
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@824029 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--share/www/script/couch.js5
-rw-r--r--share/www/script/test/replication.js24
-rw-r--r--src/couchdb/couch_httpd.erl4
-rw-r--r--src/couchdb/couch_rep.erl47
-rw-r--r--src/couchdb/couch_rep_httpc.erl16
5 files changed, 79 insertions, 17 deletions
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index f6c1199a..86465c95 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -387,9 +387,12 @@ CouchDB.getVersion = function() {
CouchDB.replicate = function(source, target, rep_options) {
rep_options = rep_options || {};
var headers = rep_options.headers || {};
+ var body = rep_options.body || {};
+ body.source = source;
+ body.target = target;
CouchDB.last_req = CouchDB.request("POST", "/_replicate", {
headers: headers,
- body: JSON.stringify({source: source, target: target})
+ body: JSON.stringify(body)
});
CouchDB.maybeThrowError(CouchDB.last_req);
return JSON.parse(CouchDB.last_req.responseText);
diff --git a/share/www/script/test/replication.js b/share/www/script/test/replication.js
index 210ffa2c..78678937 100644
--- a/share/www/script/test/replication.js
+++ b/share/www/script/test/replication.js
@@ -277,4 +277,28 @@ couchTests.replication = function(debug) {
T(result2.no_changes == true);
T(result2.session_id == result.session_id);
}
+
+ // test optional automatic creation of the target db
+
+ var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
+ var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
+
+ dbA.deleteDb();
+ dbA.createDb();
+ dbB.deleteDb();
+
+ // local
+ CouchDB.replicate(dbA.name, "test_suite_db_b", {
+ body: {"create_target": true}
+ });
+ TEquals("test_suite_db_b", dbB.info().db_name,
+ "Target database should exist");
+
+ // remote
+ dbB.deleteDb();
+ CouchDB.replicate(dbA.name, "http://" + CouchDB.host + "/test_suite_db_b", {
+ body: {"create_target": true}
+ });
+ TEquals("test_suite_db_b", dbB.info().db_name,
+ "Target database should exist");
};
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index b4e8793b..70b31d16 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -362,7 +362,9 @@ etag_respond(Req, CurrentEtag, RespFun) ->
RespFun()
end.
-verify_is_server_admin(#httpd{user_ctx=#user_ctx{roles=Roles}}) ->
+verify_is_server_admin(#httpd{user_ctx=UserCtx}) ->
+ verify_is_server_admin(UserCtx);
+verify_is_server_admin(#user_ctx{roles=Roles}) ->
case lists:member(<<"_admin">>, Roles) of
true -> ok;
false -> throw({unauthorized, <<"You are not a server admin.">>})
diff --git a/src/couchdb/couch_rep.erl b/src/couchdb/couch_rep.erl
index 14ecdf53..1a894523 100644
--- a/src/couchdb/couch_rep.erl
+++ b/src/couchdb/couch_rep.erl
@@ -28,6 +28,7 @@
source,
target,
continuous,
+ create_target,
init_args,
checkpoint_scheduled = nil,
@@ -102,9 +103,10 @@ do_init([RepId, {PostProps}, UserCtx] = InitArgs) ->
TargetProps = proplists:get_value(<<"target">>, PostProps),
Continuous = proplists:get_value(<<"continuous">>, PostProps, false),
+ CreateTarget = proplists:get_value(<<"create_target">>, PostProps, false),
Source = open_db(SourceProps, UserCtx),
- Target = open_db(TargetProps, UserCtx),
+ Target = open_db(TargetProps, UserCtx, CreateTarget),
SourceLog = open_replication_log(Source, RepId),
TargetLog = open_replication_log(Target, RepId),
@@ -143,6 +145,7 @@ do_init([RepId, {PostProps}, UserCtx] = InitArgs) ->
source = Source,
target = Target,
continuous = Continuous,
+ create_target = CreateTarget,
init_args = InitArgs,
stats = Stats,
checkpoint_scheduled = nil,
@@ -375,6 +378,17 @@ has_session_id(SessionId, [{Props} | Rest]) ->
has_session_id(SessionId, Rest)
end.
+maybe_append_options(Options, Props) ->
+ lists:foldl(fun(Option, Acc) ->
+ Acc ++
+ case proplists:get_value(Option, Props, false) of
+ true ->
+ "+" ++ ?b2l(Option);
+ false ->
+ ""
+ end
+ end, [], Options).
+
make_replication_id({Props}, UserCtx) ->
%% funky algorithm to preserve backwards compatibility
{ok, HostName} = inet:gethostname(),
@@ -382,12 +396,8 @@ make_replication_id({Props}, UserCtx) ->
Src = get_rep_endpoint(UserCtx, proplists:get_value(<<"source">>, Props)),
Tgt = get_rep_endpoint(UserCtx, proplists:get_value(<<"target">>, Props)),
Base = couch_util:to_hex(erlang:md5(term_to_binary([HostName, Src, Tgt]))),
- Extension = case proplists:get_value(<<"continuous">>, Props, false) of
- true ->
- "+continuous";
- false ->
- ""
- end,
+ Extension = maybe_append_options(
+ [<<"continuous">>, <<"create_target">>], Props),
{Base, Extension}.
maybe_add_trailing_slash(Url) ->
@@ -432,7 +442,10 @@ open_replication_log(Db, RepId) ->
#doc{id=DocId}
end.
-open_db({Props}, _UserCtx) ->
+open_db(Props, UserCtx) ->
+ open_db(Props, UserCtx, false).
+
+open_db({Props}, _UserCtx, CreateTarget) ->
Url = maybe_add_trailing_slash(proplists:get_value(<<"url">>, Props)),
{AuthProps} = proplists:get_value(<<"auth">>, Props, {[]}),
{BinHeaders} = proplists:get_value(<<"headers">>, Props, {[]}),
@@ -443,12 +456,18 @@ open_db({Props}, _UserCtx) ->
auth = AuthProps,
headers = lists:ukeymerge(1, Headers, DefaultHeaders)
},
- couch_rep_httpc:db_exists(Db);
-open_db(<<"http://",_/binary>>=Url, _) ->
- open_db({[{<<"url">>,Url}]}, []);
-open_db(<<"https://",_/binary>>=Url, _) ->
- open_db({[{<<"url">>,Url}]}, []);
-open_db(<<DbName/binary>>, UserCtx) ->
+ couch_rep_httpc:db_exists(Db, CreateTarget);
+open_db(<<"http://",_/binary>>=Url, _, CreateTarget) ->
+ open_db({[{<<"url">>,Url}]}, [], CreateTarget);
+open_db(<<"https://",_/binary>>=Url, _, CreateTarget) ->
+ open_db({[{<<"url">>,Url}]}, [], CreateTarget);
+open_db(<<DbName/binary>>, UserCtx, CreateTarget) ->
+ ok = couch_httpd:verify_is_server_admin(UserCtx),
+ case CreateTarget of
+ true -> couch_server:create(DbName, [{user_ctx, UserCtx}]);
+ false -> ok
+ end,
+
case couch_db:open(DbName, [{user_ctx, UserCtx}]) of
{ok, Db} ->
couch_db:monitor(Db),
diff --git a/src/couchdb/couch_rep_httpc.erl b/src/couchdb/couch_rep_httpc.erl
index b714be6b..ba863746 100644
--- a/src/couchdb/couch_rep_httpc.erl
+++ b/src/couchdb/couch_rep_httpc.erl
@@ -14,7 +14,7 @@
-include("couch_db.hrl").
-include("../ibrowse/ibrowse.hrl").
--export([db_exists/1, full_url/1, request/1, spawn_worker_process/1,
+-export([db_exists/1, db_exists/2, full_url/1, request/1, spawn_worker_process/1,
spawn_link_worker_process/1]).
request(Req) when is_record(Req, http_db) ->
@@ -59,7 +59,16 @@ do_request(Req) ->
db_exists(Req) ->
db_exists(Req, Req#http_db.url).
+db_exists(Req, true) ->
+ db_exists(Req, Req#http_db.url, true);
+
+db_exists(Req, false) ->
+ db_exists(Req, Req#http_db.url, false);
+
db_exists(Req, CanonicalUrl) ->
+ db_exists(Req, CanonicalUrl, false).
+
+db_exists(Req, CanonicalUrl, CreateDB) ->
#http_db{
auth = Auth,
headers = Headers0,
@@ -71,6 +80,11 @@ db_exists(Req, CanonicalUrl) ->
{OAuthProps} ->
[oauth_header(Url, [], head, OAuthProps) | Headers0]
end,
+ case CreateDB of
+ true ->
+ catch ibrowse:send_req(Url, Headers, put);
+ _Else -> ok
+ end,
case catch ibrowse:send_req(Url, Headers, head) of
{ok, "200", _, _} ->
Req#http_db{url = CanonicalUrl};