Add the maxTotalSize quota to limit total size of a namespace
For some scenarios it may be more convenient to only define quota on a
namespace level. In that case only the total size of all repositories
will be enforced.
This can also be combined with the maxRepoSize to enforce both the total
size of all repositories in a namespace and the max size of each of
them.
Change-Id: Iee40ea4740dbf64e03f14349caf32fac331d292d
diff --git a/pom.xml b/pom.xml
index 1a75f9d..d0072e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
<properties>
<Gerrit-ApiType>plugin</Gerrit-ApiType>
- <Gerrit-ApiVersion>2.9-SNAPSHOT</Gerrit-ApiVersion>
+ <Gerrit-ApiVersion>2.10-SNAPSHOT</Gerrit-ApiVersion>
</properties>
<build>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java b/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java
index 6e8b0cb..926b660 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java
@@ -16,12 +16,14 @@
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Ordering;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
@@ -68,15 +70,18 @@
private final QuotaFinder quotaFinder;
private final LoadingCache<Project.NameKey, AtomicLong> cache;
+ private final ProjectCache projectCache;
private final SitePaths site;
private final Path basePath;
@Inject
MaxRepositorySizeQuota(QuotaFinder quotaFinder,
@Named(CACHE_NAME) LoadingCache<Project.NameKey, AtomicLong> cache,
+ ProjectCache projectCache,
SitePaths site, @GerritServerConfig final Config cfg) {
this.quotaFinder = quotaFinder;
this.cache = cache;
+ this.projectCache = projectCache;
this.site = site;
basePath = site.resolve(cfg.getString("gerrit", null, "basePath")).toPath();
}
@@ -89,12 +94,30 @@
}
Long maxRepoSize = quotaSection.getMaxRepoSize();
- if (maxRepoSize == null) {
+ Long maxTotalSize = quotaSection.getMaxTotalSize();
+ if (maxRepoSize == null && maxTotalSize == null) {
return;
}
try {
- long maxPackSize = Math.max(0, maxRepoSize - cache.get(project).get());
+ Long maxPackSize1 = null;
+ if (maxRepoSize != null) {
+ maxPackSize1 = Math.max(0, maxRepoSize - cache.get(project).get());
+ }
+
+ Long maxPackSize2 = null;
+ if (maxTotalSize != null) {
+ long totalSize = 0;
+ for (Project.NameKey p : projectCache.all()) {
+ if (quotaSection.matches(p)) {
+ totalSize += cache.get(p).get();
+ }
+ }
+ maxPackSize2 = Math.max(0, maxTotalSize - totalSize);
+ }
+
+ long maxPackSize = Ordering.<Long> natural().nullsLast().min(
+ maxPackSize1, maxPackSize2);
rp.setMaxPackSizeLimit(maxPackSize);
} catch (ExecutionException e) {
log.warn("Couldn't setMaxPackSizeLimit on receive-pack for "
diff --git a/src/main/java/com/googlesource/gerrit/plugins/quota/QuotaSection.java b/src/main/java/com/googlesource/gerrit/plugins/quota/QuotaSection.java
index 5a3700f..6419258 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/quota/QuotaSection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/quota/QuotaSection.java
@@ -22,6 +22,7 @@
public static final String QUOTA = "quota";
public static final String KEY_MAX_PROJECTS = "maxProjects";
public static final String KEY_MAX_REPO_SIZE = "maxRepoSize";
+ public static final String KEY_MAX_TOTAL_SIZE = "maxTotalSize";
private final Config cfg;
private final String namespace;
@@ -58,4 +59,11 @@
}
return cfg.getLong(QUOTA, namespace, KEY_MAX_REPO_SIZE, Long.MAX_VALUE);
}
+
+ public Long getMaxTotalSize() {
+ if (!cfg.getNames(QUOTA, namespace).contains(KEY_MAX_TOTAL_SIZE)) {
+ return null;
+ }
+ return cfg.getLong(QUOTA, namespace, KEY_MAX_TOTAL_SIZE, Long.MAX_VALUE);
+ }
}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 9488cbc..971053c 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -14,6 +14,9 @@
[quota "public/*"]
maxProjects = 100
maxRepoSize = 10 m
+ [quota "customerX/*"]
+ maxProjects = 20
+ maxTotalSize = 200 m
```
<a id="maxProjects">
@@ -28,6 +31,20 @@
reference file is counted as 41 bytes although it typically occupies a
block of 4K in the file system.
+<a id="maxTotalSize">
+`quota.<namespace>.maxTotalSize`
+: The maximum total file size of all repositories in this namespace.
+This is the sum of sizes of all files in all Git repositories in this
+namespace.
+
+If both "maxRepoSize" and "maxTotalSize" are defined in a quota section
+then the more limiting quota will apply. For example, if the remaining
+repository size of a repository (based on the "maxRepoSize" and
+currently occupied space of that repository) is 2m and the remaining
+total size (based on the "maxTotalSize" and currently occupied space of
+all repositoris under that namespace) is 1m then the 1m is the remaining
+size for that repository.
+
A namespace can be specified as
* exact project name (`plugins/myPlugin`): Defines a quota for one project.
@@ -69,3 +86,29 @@
[quota "?/*"]
maxProjects = 5
```
+
+Example: Allow the creation of 10 projects in folder 'test/*' and set
+the quota of 2m for each of them
+```
+ [quota "test/*"]
+ maxProjects = 10
+ maxRepoSize = 2 m
+```
+
+Example: Allow the creation of 10 projects in folder 'test/*' and set
+a quota of 20m for the total size of all repositories
+```
+ [quota "test/*"]
+ maxProjects = 10
+ maxTotalSize = 20 m
+```
+
+Example: Allow the creation of 10 projects in folder 'test/*' and set
+a quota of 20m for the total size of all repositories. In addition make
+sure that each individual repository cannot exceed 3m
+```
+ [quota "test/*"]
+ maxProjects = 10
+ maxRepoSize = 3 m
+ maxTotalSize = 20 m
+```