- now that we have to configure usher's configuration file anyway,

we can skip the explicit configuration of its host and admin
  password, as we can directly read that from the configuration file
  itself
- expand the SyncMonotone plugin a bit to where this actually becomes
  useful, i.e.  create an accompanying key for each created database
  and also add some initial database-specific configuration
- update the config docs in idf.php-dist to reflect the changes and
  add more details about the inner workings of the SyncMonotone plugin
This commit is contained in:
Thomas Keller
2010-08-29 23:01:25 +00:00
parent 194dcad0e3
commit b648e6f7a7
5 changed files with 170 additions and 63 deletions

View File

@@ -41,8 +41,15 @@ class IDF_Plugin_SyncMonotone
}
/**
* Run mtn init command to create the corresponding monotone
* repository and add the database to the configured usher instance
* Four steps to setup a new monotone project:
*
* 1) run mtn db init to initialize a new database underknees
* 'mtn_repositories'
* 2) create a new server key in the same directory
* 3) write monotonerc for access control
* 4) add the database as new local server in the usher configuration
* 5) reload the running usher instance so it acknowledges the new
* server
*
* @param IDF_Project
*/
@@ -52,45 +59,113 @@ class IDF_Plugin_SyncMonotone
return;
}
$repotempl = Pluf::f('mtn_repositories', false);
if ($repotempl === false) {
$projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) {
throw new IDF_Scm_Exception(
'"mtn_repositories" must be defined in your configuration file.'
);
}
$usher_config = Pluf::f('mtn_usher', array());
if (!array_key_exists('rcfile', $usher_config) ||
!is_writable($usher_config['rcfile'])) {
$usher_config = Pluf::f('mtn_usher_conf', false);
if (!$usher_config || !is_writable($usher_config)) {
throw new IDF_Scm_Exception(
'"rcfile" in "mtn_usher" does not exist or is not writable.'
'"mtn_usher_conf" does not exist or is not writable.'
);
}
$shortname = $project->shortname;
$dbfile = sprintf($repotempl, $shortname);
if (file_exists($dbfile)) {
$projectpath = sprintf($projecttempl, $shortname);
if (file_exists($projectpath)) {
throw new IDF_Scm_Exception(sprintf(
__('The repository %s already exists.'), $dbfile
__('The project path %s already exists.'), $projectpath
));
}
$return = 0;
$output = array();
if (!mkdir($projectpath)) {
throw new IDF_Scm_Exception(sprintf(
__('The project path %s could not be created.'), $projectpath
));
}
//
// step 1) create a new database
//
$dbfile = $projectpath.'/database.mtn';
$cmd = sprintf(
Pluf::f('mtn_path', 'mtn').' db init -d %s',
escapeshellarg($dbfile)
);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$ll = exec($cmd, $output, $return);
$ll = exec($cmd, $output = array(), $return = 0);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf(
__('Could not create repository %s - please check '.
'your error log for details.'),
$dbfile
__('The database file %s could not be created.'), $dbfile
));
}
$usher_rc = file_get_contents($usher_config['rcfile']);
//
// step 2) create a server key
//
// try to parse the key's domain part from the remote_url's host
// name, otherwise fall back to the configured Apache server name
$server = $_SERVER['SERVER_NAME'];
$remote_url = Pluf::f('mtn_remote_url');
if (($parsed = parse_url($remote_url)) !== false &&
!empty($parsed['host'])) {
$server = $parsed['host'];
}
$keyname = $shortname.'-server@'.$server;
$cmd = sprintf(
Pluf::f('mtn_path', 'mtn').' au genkey --confdir=%s %s ""',
escapeshellarg($projectpath),
escapeshellarg($keyname)
);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$ll = exec($cmd, $output = array(), $return = 0);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf(
__('The server key %s could not be created.'), $keyname
));
}
//
// step 3) write monotonerc for access control
// FIXME: netsync access control is still missing!
//
$monotonerc =<<<END
function get_remote_automate_permitted(key_identity, command, options)
local read_only_commands = {
"get_corresponding_path", "get_content_changed", "tags", "branches",
"common_ancestors", "packet_for_fdelta", "packet_for_fdata",
"packets_for_certs", "packet_for_rdata", "get_manifest_of",
"get_revision", "select", "graph", "children", "parents", "roots",
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
"descendents", "ancestors", "heads", "get_file_of", "get_file",
"interface_version", "get_attributes", "content_diff",
"file_merge", "show_conflicts", "certs", "keys"
}
for _,v in ipairs(read_only_commands) do
if (v == command[1]) then
return true
end
end
return false
end
END;
$rcfile = $projectpath.'/monotonerc';
// FIXME: sanity
$fp = fopen($rcfile, 'w');
fwrite($fp, $monotonerc);
fclose($fp);
//
// step 4) read in and append the usher config with the new server
//
$usher_rc = file_get_contents($usher_config);
$parsed_config = array();
try {
$parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
@@ -98,7 +173,7 @@ class IDF_Plugin_SyncMonotone
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse usher configuration in "%s": %s'),
$usher_config['rcfile'], $e->getMessage()
$usher_config, $e->getMessage()
));
}
@@ -121,17 +196,23 @@ class IDF_Plugin_SyncMonotone
$new_server = array(
array('key' => 'server', 'values' => array($shortname)),
array('key' => 'local', 'values' => array('-d', $dbfile)),
array('key' => 'local', 'values' => array(
'--confdir', $projectpath,
'-d', $dbfile
)),
);
$parsed_config[] = $new_server;
$usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config);
// FIXME: more sanity - what happens on failing writes?
$fp = fopen($usher_config['rcfile'], 'w');
$fp = fopen($usher_config, 'w');
fwrite($fp, $usher_rc);
fclose($fp);
//
// step 5) reload usher to pick up the new configuration
//
IDF_Scm_Monotone_Usher::reload();
}
}