Merge changes from topic 'fix-group-reference' into stable-2.14
* changes:
Support plugin group reference with inheritance
Fix PluginConfig.setGroupReference method
Align group reference from plugin with core group reference
Add method to convert a group reference into a configuration value
Add method to extract group name from a configured value
Add method to check if configured value is a group reference
Extract group reference prefix into a constant
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 2976e96..1efe839 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -912,12 +912,15 @@
Plugins can refer to groups so that when they are renamed, the project
config will also be updated in this section. The proper format to use is
-the string representation of a GroupReference, as shown below.
+the same as for any other group reference in the `project.config`, as shown below.
----
-Group[group_name / group_uuid]
+group group_name
----
+The file `groups` must also contains the mapping of the group name and its UUID,
+refer to link:config-project-config.html#file-groups[file groups]
+
[[project-specific-configuration]]
== Project Specific Configuration in own config file
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
index 8362281..dc22d62 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
@@ -14,10 +14,14 @@
package com.google.gerrit.common.data;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.AccountGroup;
/** Describes a group within a projects {@link AccessSection}s. */
public class GroupReference implements Comparable<GroupReference> {
+
+ private static final String PREFIX = "group ";
+
/** @return a new reference to the given group description. */
public static GroupReference forGroup(AccountGroup group) {
return new GroupReference(group.getGroupUUID(), group.getName());
@@ -27,10 +31,16 @@
return new GroupReference(group.getGroupUUID(), group.getName());
}
- public static GroupReference fromString(String ref) {
- String name = ref.substring(ref.indexOf("[") + 1, ref.lastIndexOf("/")).trim();
- String uuid = ref.substring(ref.lastIndexOf("/") + 1, ref.lastIndexOf("]")).trim();
- return new GroupReference(new AccountGroup.UUID(uuid), name);
+ public static boolean isGroupReference(String configValue) {
+ return configValue != null && configValue.startsWith(PREFIX);
+ }
+
+ @Nullable
+ public static String extractGroupName(String configValue) {
+ if (!isGroupReference(configValue)) {
+ return null;
+ }
+ return configValue.substring(PREFIX.length()).trim();
}
protected String uuid;
@@ -78,6 +88,10 @@
return o instanceof GroupReference && compareTo((GroupReference) o) == 0;
}
+ public String toConfigValue() {
+ return PREFIX + name;
+ }
+
@Override
public String toString() {
return "Group[" + getName() + " / " + getUUID() + "]";
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
index 9265830..9098ec3 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
@@ -204,8 +204,7 @@
r.append(' ');
}
- r.append("group ");
- r.append(getGroup().getName());
+ r.append(getGroup().toConfigValue());
return r.toString();
}
@@ -238,7 +237,7 @@
src = src.substring("+force ".length()).trim();
}
- if (mightUseRange && !src.startsWith("group ")) {
+ if (mightUseRange && !GroupReference.isGroupReference(src)) {
int sp = src.indexOf(' ');
String range = src.substring(0, sp);
@@ -254,10 +253,10 @@
src = src.substring(sp + 1).trim();
}
- if (src.startsWith("group ")) {
- src = src.substring(6).trim();
+ String groupName = GroupReference.extractGroupName(src);
+ if (groupName != null) {
GroupReference group = new GroupReference();
- group.setName(src);
+ group.setName(groupName);
rule.setGroup(group);
} else {
throw new IllegalArgumentException("Rule must include group: " + orig);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfig.java
index 1b12495..fd1a12c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfig.java
@@ -17,6 +17,7 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectState;
import java.util.Arrays;
@@ -56,11 +57,16 @@
cfg = copyConfig(cfg);
for (String name : parentPluginConfig.cfg.getNames(PLUGIN, pluginName)) {
if (!allNames.contains(name)) {
- cfg.setStringList(
- PLUGIN,
- pluginName,
- name,
- Arrays.asList(parentPluginConfig.cfg.getStringList(PLUGIN, pluginName, name)));
+ List<String> values =
+ Arrays.asList(parentPluginConfig.cfg.getStringList(PLUGIN, pluginName, name));
+ for (String value : values) {
+ GroupReference groupRef =
+ parentPluginConfig.projectConfig.getGroup(GroupReference.extractGroupName(value));
+ if (groupRef != null) {
+ projectConfig.resolve(groupRef);
+ }
+ }
+ cfg.setStringList(PLUGIN, pluginName, name, values);
}
}
}
@@ -152,4 +158,13 @@
public Set<String> getNames() {
return cfg.getNames(PLUGIN, pluginName, true);
}
+
+ public GroupReference getGroupReference(String name) {
+ return projectConfig.getGroup(GroupReference.extractGroupName(getString(name)));
+ }
+
+ public void setGroupReference(String name, GroupReference value) {
+ GroupReference groupRef = projectConfig.resolve(value);
+ setString(name, groupRef.toConfigValue());
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index 61d8cfe..e3299d9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -182,6 +182,7 @@
private boolean checkReceivedObjects;
private Set<String> sectionsWithUnknownPermissions;
private boolean hasLegacyPermissions;
+ private Map<String, GroupReference> groupsByName;
public static ProjectConfig read(MetaDataUpdate update)
throws IOException, ConfigInvalidException {
@@ -402,7 +403,13 @@
}
public GroupReference resolve(GroupReference group) {
- return groupList.resolve(group);
+ GroupReference groupRef = groupList.resolve(group);
+ if (groupRef != null
+ && groupRef.getUUID() != null
+ && !groupsByName.containsKey(groupRef.getName())) {
+ groupsByName.put(groupRef.getName(), groupRef);
+ }
+ return groupRef;
}
/** @return the group reference, if the group is used by at least one rule. */
@@ -410,6 +417,14 @@
return groupList.byUUID(uuid);
}
+ /**
+ * @return the group reference corresponding to the specified group name if the group is used by
+ * at least one rule or plugin value.
+ */
+ public GroupReference getGroup(String groupName) {
+ return groupsByName.get(groupName);
+ }
+
/** @return set of all groups used by this configuration. */
public Set<AccountGroup.UUID> getAllGroupUUIDs() {
return groupList.uuids();
@@ -473,7 +488,7 @@
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
readGroupList();
- Map<String, GroupReference> groupsByName = mapGroupReferences();
+ groupsByName = mapGroupReferences();
rulesId = getObjectId("rules.pl");
Config rc = readConfig(PROJECT_CONFIG);
@@ -515,11 +530,11 @@
p.setDefaultDashboard(rc.getString(DASHBOARD, null, KEY_DEFAULT));
p.setLocalDefaultDashboard(rc.getString(DASHBOARD, null, KEY_LOCAL_DEFAULT));
- loadAccountsSection(rc, groupsByName);
- loadContributorAgreements(rc, groupsByName);
- loadAccessSections(rc, groupsByName);
+ loadAccountsSection(rc);
+ loadContributorAgreements(rc);
+ loadAccessSections(rc);
loadBranchOrderSection(rc);
- loadNotifySections(rc, groupsByName);
+ loadNotifySections(rc);
loadLabelSections(rc);
loadCommentLinkSections(rc);
loadSubscribeSections(rc);
@@ -528,13 +543,13 @@
loadReceiveSection(rc);
}
- private void loadAccountsSection(Config rc, Map<String, GroupReference> groupsByName) {
+ private void loadAccountsSection(Config rc) {
accountsSection = new AccountsSection();
accountsSection.setSameGroupVisibility(
loadPermissionRules(rc, ACCOUNTS, null, KEY_SAME_GROUP_VISIBILITY, groupsByName, false));
}
- private void loadContributorAgreements(Config rc, Map<String, GroupReference> groupsByName) {
+ private void loadContributorAgreements(Config rc) {
contributorAgreements = new HashMap<>();
for (String name : rc.getSubsections(CONTRIBUTOR_AGREEMENT)) {
ContributorAgreement ca = getContributorAgreement(name, true);
@@ -594,7 +609,7 @@
* type = submitted_changes
* </pre>
*/
- private void loadNotifySections(Config rc, Map<String, GroupReference> groupsByName) {
+ private void loadNotifySections(Config rc) {
notifySections = new HashMap<>();
for (String sectionName : rc.getSubsections(NOTIFY)) {
NotifyConfig n = new NotifyConfig();
@@ -607,8 +622,8 @@
n.setHeader(rc.getEnum(NOTIFY, sectionName, KEY_HEADER, NotifyConfig.Header.BCC));
for (String dst : rc.getStringList(NOTIFY, sectionName, KEY_EMAIL)) {
- if (dst.startsWith("group ")) {
- String groupName = dst.substring(6).trim();
+ String groupName = GroupReference.extractGroupName(dst);
+ if (groupName != null) {
GroupReference ref = groupsByName.get(groupName);
if (ref == null) {
ref = new GroupReference(null, groupName);
@@ -639,7 +654,7 @@
}
}
- private void loadAccessSections(Config rc, Map<String, GroupReference> groupsByName) {
+ private void loadAccessSections(Config rc) {
accessSections = new HashMap<>();
sectionsWithUnknownPermissions = new HashSet<>();
for (String refName : rc.getSubsections(ACCESS)) {
@@ -940,17 +955,15 @@
pluginConfigs.put(plugin, pluginConfig);
for (String name : rc.getNames(PLUGIN, plugin)) {
String value = rc.getString(PLUGIN, plugin, name);
- if (value.startsWith("Group[")) {
- GroupReference refFromString = GroupReference.fromString(value);
- GroupReference ref = groupList.byUUID(refFromString.getUUID());
+ String groupName = GroupReference.extractGroupName(value);
+ if (groupName != null) {
+ GroupReference ref = groupsByName.get(groupName);
if (ref == null) {
- ref = refFromString;
error(
new ValidationError(
- PROJECT_CONFIG,
- "group \"" + ref.getName() + "\" not in " + GroupList.FILE_NAME));
+ PROJECT_CONFIG, "group \"" + groupName + "\" not in " + GroupList.FILE_NAME));
}
- rc.setString(PLUGIN, plugin, name, ref.toString());
+ rc.setString(PLUGIN, plugin, name, value);
}
pluginConfig.setStringList(
PLUGIN, plugin, name, Arrays.asList(rc.getStringList(PLUGIN, plugin, name)));
@@ -1354,11 +1367,12 @@
Config pluginConfig = e.getValue();
for (String name : pluginConfig.getNames(PLUGIN, plugin)) {
String value = pluginConfig.getString(PLUGIN, plugin, name);
- if (value.startsWith("Group[")) {
- GroupReference ref = resolve(GroupReference.fromString(value));
- if (ref.getUUID() != null) {
+ String groupName = GroupReference.extractGroupName(value);
+ if (groupName != null) {
+ GroupReference ref = groupsByName.get(groupName);
+ if (ref != null && ref.getUUID() != null) {
keepGroups.add(ref.getUUID());
- pluginConfig.setString(PLUGIN, plugin, name, ref.toString());
+ pluginConfig.setString(PLUGIN, plugin, name, "group " + ref.getName());
}
}
rc.setStringList(
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
index ab68c10..2d927be 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
@@ -27,8 +27,10 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -325,6 +327,155 @@
+ " read = group Developers\n");
}
+ @Test
+ public void readExistingPluginConfig() throws Exception {
+ RevCommit rev =
+ util.commit(
+ util.tree( //
+ util.file(
+ "project.config",
+ util.blob(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + " key1 = value1\n" //
+ + " key2 = value2a\n"
+ + " key2 = value2b\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+ assertThat(pluginCfg.getNames().size()).isEqualTo(2);
+ assertThat(pluginCfg.getString("key1")).isEqualTo("value1");
+ assertThat(pluginCfg.getStringList(("key2"))).isEqualTo(new String[] {"value2a", "value2b"});
+ }
+
+ @Test
+ public void readUnexistingPluginConfig() throws Exception {
+ ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
+ cfg.load(db);
+ PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+ assertThat(pluginCfg.getNames()).isEmpty();
+ }
+
+ @Test
+ public void editPluginConfig() throws Exception {
+ RevCommit rev =
+ util.commit(
+ util.tree( //
+ util.file(
+ "project.config",
+ util.blob(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + " key1 = value1\n" //
+ + " key2 = value2a\n" //
+ + " key2 = value2b\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+ pluginCfg.setString("key1", "updatedValue1");
+ pluginCfg.setStringList("key2", Arrays.asList("updatedValue2a", "updatedValue2b"));
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + "\tkey1 = updatedValue1\n" //
+ + "\tkey2 = updatedValue2a\n" //
+ + "\tkey2 = updatedValue2b\n");
+ }
+
+ @Test
+ public void readPluginConfigGroupReference() throws Exception {
+ RevCommit rev =
+ util.commit(
+ util.tree( //
+ util.file("groups", util.blob(group(developers))), //
+ util.file(
+ "project.config",
+ util.blob(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + "key1 = "
+ + developers.toConfigValue()
+ + "\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+ assertThat(pluginCfg.getNames().size()).isEqualTo(1);
+ assertThat(pluginCfg.getGroupReference("key1")).isEqualTo(developers);
+ }
+
+ @Test
+ public void readPluginConfigGroupReferenceNotInGroupsFile() throws Exception {
+ RevCommit rev =
+ util.commit(
+ util.tree( //
+ util.file("groups", util.blob(group(developers))), //
+ util.file(
+ "project.config",
+ util.blob(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + "key1 = "
+ + staff.toConfigValue()
+ + "\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ assertThat(cfg.getValidationErrors()).hasSize(1);
+ assertThat(Iterables.getOnlyElement(cfg.getValidationErrors()).getMessage())
+ .isEqualTo(
+ "project.config: group \"" + staff.getName() + "\" not in " + GroupList.FILE_NAME);
+ }
+
+ @Test
+ public void editPluginConfigGroupReference() throws Exception {
+ RevCommit rev =
+ util.commit(
+ util.tree( //
+ util.file("groups", util.blob(group(developers))), //
+ util.file(
+ "project.config",
+ util.blob(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + "key1 = "
+ + developers.toConfigValue()
+ + "\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+ assertThat(pluginCfg.getNames().size()).isEqualTo(1);
+ assertThat(pluginCfg.getGroupReference("key1")).isEqualTo(developers);
+
+ pluginCfg.setGroupReference("key1", staff);
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo(
+ "" //
+ + "[plugin \"somePlugin\"]\n" //
+ + "\tkey1 = "
+ + staff.toConfigValue()
+ + "\n");
+ assertThat(text(rev, "groups"))
+ .isEqualTo(
+ "# UUID\tGroup Name\n" //
+ + "#\n" //
+ + staff.getUUID().get()
+ + " \t"
+ + staff.getName()
+ + "\n");
+ }
+
private ProjectConfig read(RevCommit rev) throws IOException, ConfigInvalidException {
ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
cfg.load(db, rev);