Support to inherit project level plugin config for own config file

When reading the project level plugin configuration that is stored in
an own configuration file it is now possible to get missing
configuration values inherited from the parent projects.

Change-Id: Idff2c02adf55524b56a43d6dd23ec6ebc468fedb
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2013-10-30 14:50:01 +01:00
parent 705f284863
commit 762da38661
6 changed files with 144 additions and 4 deletions

View File

@@ -655,6 +655,20 @@ String[] reviewers = cfg.getProjectPluginConfig(project, "default-reviewer")
.getStringList("branch", "refs/heads/master", "reviewer"); .getStringList("branch", "refs/heads/master", "reviewer");
---- ----
It is also possible to get missing configuration parameters inherited
from the parent projects:
[source,java]
----
@Inject
private com.google.gerrit.server.config.PluginConfigFactory cfg;
[...]
String[] reviewers = cfg.getFromPluginConfigWithInheritance(project, "default-reviewer")
.getStringList("branch", "refs/heads/master", "reviewer");
----
Project owners can edit the project configuration by fetching the Project owners can edit the project configuration by fetching the
`refs/meta/config` branch, editing the `<plugin-name>.config` file and `refs/meta/config` branch, editing the `<plugin-name>.config` file and
pushing the commit back. pushing the commit back.

View File

@@ -106,10 +106,15 @@ public class GitUtil {
} }
public static Git cloneProject(String url) throws GitAPIException, IOException { public static Git cloneProject(String url) throws GitAPIException, IOException {
return cloneProject(url, true);
}
public static Git cloneProject(String url, boolean checkout) throws GitAPIException, IOException {
final File gitDir = TempFileUtil.createTempDirectory(); final File gitDir = TempFileUtil.createTempDirectory();
final CloneCommand cloneCmd = Git.cloneRepository(); final CloneCommand cloneCmd = Git.cloneRepository();
cloneCmd.setURI(url); cloneCmd.setURI(url);
cloneCmd.setDirectory(gitDir); cloneCmd.setDirectory(gitDir);
cloneCmd.setNoCheckout(!checkout);
return cloneCmd.call(); return cloneCmd.call();
} }

View File

@@ -28,6 +28,7 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.git.PushOneCommit; import com.google.gerrit.acceptance.git.PushOneCommit;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@@ -54,8 +55,12 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
@Inject @Inject
private ProjectCache projectCache; private ProjectCache projectCache;
@Inject
private AllProjectsNameProvider allProjects;
private ReviewDb db; private ReviewDb db;
private TestAccount admin; private TestAccount admin;
private SshSession sshSession;
private String project; private String project;
private Git git; private Git git;
@@ -63,7 +68,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
public void setUp() throws Exception { public void setUp() throws Exception {
admin = accounts.admin(); admin = accounts.admin();
initSsh(admin); initSsh(admin);
SshSession sshSession = new SshSession(server, admin); sshSession = new SshSession(server, admin);
project = "p"; project = "p";
createProject(sshSession, project, null, true); createProject(sshSession, project, null, true);
@@ -99,4 +104,44 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
ProjectState state = projectCache.get(new Project.NameKey(project)); ProjectState state = projectCache.get(new Project.NameKey(project));
assertEquals("", state.getConfig("test.config").get().toText()); assertEquals("", state.getConfig("test.config").get().toText());
} }
@Test
public void withInheritance() throws GitAPIException, IOException {
String configName = "test.config";
Config parentCfg = new Config();
parentCfg.setString("s1", null, "k1", "parentValue1");
parentCfg.setString("s1", null, "k2", "parentValue2");
parentCfg.setString("s2", "ss", "k3", "parentValue3");
parentCfg.setString("s2", "ss", "k4", "parentValue4");
Git parentGit =
cloneProject(sshSession.getUrl() + "/" + allProjects.get().get(), false);
fetch(parentGit, GitRepositoryManager.REF_CONFIG + ":refs/heads/config");
checkout(parentGit, "refs/heads/config");
PushOneCommit push =
new PushOneCommit(db, admin.getIdent(), "Create Project Level Config",
configName, parentCfg.toText());
push.to(parentGit, GitRepositoryManager.REF_CONFIG);
Config cfg = new Config();
cfg.setString("s1", null, "k1", "childValue1");
cfg.setString("s2", "ss", "k3", "childValue2");
push = new PushOneCommit(db, admin.getIdent(), "Create Project Level Config",
configName, cfg.toText());
push.to(git, GitRepositoryManager.REF_CONFIG);
ProjectState state = projectCache.get(new Project.NameKey(project));
Config expectedCfg = new Config();
expectedCfg.setString("s1", null, "k1", "childValue1");
expectedCfg.setString("s1", null, "k2", "parentValue2");
expectedCfg.setString("s2", "ss", "k3", "childValue2");
expectedCfg.setString("s2", "ss", "k4", "parentValue4");
assertEquals(expectedCfg.toText(), state.getConfig(configName)
.getWithInheritance().toText());
assertEquals(cfg.toText(), state.getConfig(configName).get().toText());
}
} }

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.config;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.ProjectLevelConfig;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@@ -196,10 +197,45 @@ public class PluginConfigFactory {
*/ */
public Config getProjectPluginConfig(Project.NameKey projectName, public Config getProjectPluginConfig(Project.NameKey projectName,
String pluginName) throws NoSuchProjectException { String pluginName) throws NoSuchProjectException {
return getPluginConfig(projectName, pluginName).get();
}
/**
* Returns the configuration for the specified plugin that is stored in the
* '<plugin-name>.config' file in the 'refs/meta/config' branch of the
* specified project. Parameters which are not set in the
* '<plugin-name>.config' of this project are inherited from the parent
* project's '<plugin-name>.config' files.
*
* E.g.: child project: [mySection "mySubsection"] myKey = childValue
*
* parent project: [mySection "mySubsection"] myKey = parentValue anotherKey =
* someValue
*
* return: [mySection "mySubsection"] myKey = childValue anotherKey =
* someValue
*
* @param projectName the name of the project for which the plugin
* configuration should be returned
* @param pluginName the name of the plugin for which the configuration should
* be returned
* @return the plugin configuration from the '<plugin-name>.config' file of
* the specified project with inheriting non-set parameters from the
* parent projects
* @throws NoSuchProjectException thrown if the specified project does not
* exist
*/
public Config getProjectPluginConfigWithInheritance(Project.NameKey projectName,
String pluginName) throws NoSuchProjectException {
return getPluginConfig(projectName, pluginName).getWithInheritance();
}
private ProjectLevelConfig getPluginConfig(Project.NameKey projectName,
String pluginName) throws NoSuchProjectException {
ProjectState projectState = projectCache.get(projectName); ProjectState projectState = projectCache.get(projectName);
if (projectState == null) { if (projectState == null) {
throw new NoSuchProjectException(projectName); throw new NoSuchProjectException(projectName);
} }
return projectState.getConfig(pluginName + ".config").get(); return projectState.getConfig(pluginName);
} }
} }

View File

@@ -14,19 +14,26 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import com.google.common.collect.Iterables;
import com.google.gerrit.server.project.ProjectState;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
/** Configuration file in the projects refs/meta/config branch. */ /** Configuration file in the projects refs/meta/config branch. */
public class ProjectLevelConfig extends VersionedMetaData { public class ProjectLevelConfig extends VersionedMetaData {
private final String fileName; private final String fileName;
private final ProjectState project;
private Config cfg; private Config cfg;
public ProjectLevelConfig(String fileName) { public ProjectLevelConfig(String fileName, ProjectState project) {
this.fileName = fileName; this.fileName = fileName;
this.project = project;
} }
@Override @Override
@@ -46,6 +53,39 @@ public class ProjectLevelConfig extends VersionedMetaData {
return cfg; return cfg;
} }
public Config getWithInheritance() {
Config cfgWithInheritance = new Config();
try {
cfgWithInheritance.fromText(get().toText());
} catch (ConfigInvalidException e) {
// cannot happen
}
ProjectState parent = Iterables.getFirst(project.parents(), null);
if (parent != null) {
Config parentCfg = parent.getConfig(fileName).getWithInheritance();
for (String section : parentCfg.getSections()) {
Set<String> allNames = get().getNames(section);
for (String name : parentCfg.getNames(section)) {
if (!allNames.contains(name)) {
cfgWithInheritance.setStringList(section, null, name,
Arrays.asList(parentCfg.getStringList(section, null, name)));
}
}
for (String subsection : parentCfg.getSubsections(section)) {
allNames = get().getNames(section, subsection);
for (String name : parentCfg.getNames(section, subsection)) {
if (!allNames.contains(name)) {
cfgWithInheritance.setStringList(section, subsection, name,
Arrays.asList(parentCfg.getStringList(section, subsection, name)));
}
}
}
}
}
return cfgWithInheritance;
}
@Override @Override
protected void onSave(CommitBuilder commit) throws IOException, protected void onSave(CommitBuilder commit) throws IOException,
ConfigInvalidException { ConfigInvalidException {

View File

@@ -225,7 +225,7 @@ public class ProjectState {
return configs.get(fileName); return configs.get(fileName);
} }
ProjectLevelConfig cfg = new ProjectLevelConfig(fileName); ProjectLevelConfig cfg = new ProjectLevelConfig(fileName, this);
try { try {
Repository git = gitMgr.openRepository(getProject().getNameKey()); Repository git = gitMgr.openRepository(getProject().getNameKey());
try { try {