Merge branch 'stable-2.14' into stable-2.15

* stable-2.14:
  Display Used Permits in addition to permits available
  Add readable text to the limit type

Change-Id: Id93f79428dd4ed6ab9169cfd6c0f415d72c2b305
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/ListCommand.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/ListCommand.java
index 9f6c946..e31d9bc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/ListCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/ListCommand.java
@@ -11,12 +11,9 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 package com.googlesource.gerrit.plugins.ratelimiter;
-
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
 import static com.googlesource.gerrit.plugins.ratelimiter.Module.UPLOAD_PACK_PER_HOUR;
-
 import com.google.common.cache.LoadingCache;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -30,7 +27,6 @@
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
-
 @AdminHighPriorityCommand
 @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
 @CommandMetaData(
@@ -38,13 +34,11 @@
     description = "Display rate limits statistics",
     runsAt = MASTER_OR_SLAVE)
 final class ListCommand extends SshCommand {
-  private static final String FORMAT = "%-26s %-17s %-19s %s";
+  private static final String FORMAT = "%-26s %-17s %-19s %-15s %s";
   private static final String DASHED_LINE =
-      "------------------------------------------------------------------------------";
-
+      "---------------------------------------------------------------------------------------------";
   private final LoadingCache<String, RateLimiter> uploadPackPerHour;
   private final UserResolver userResolver;
-
   @Inject
   ListCommand(
       @Named(UPLOAD_PACK_PER_HOUR) LoadingCache<String, RateLimiter> uploadPackPerHour,
@@ -52,7 +46,6 @@
     this.uploadPackPerHour = uploadPackPerHour;
     this.userResolver = userResolver;
   }
-
   @Override
   protected void run() throws UnloggedFailure {
     try {
@@ -65,6 +58,7 @@
               "Account Id/IP (username)",
               "Permits Per Hour",
               "Available Permits",
+              "Used Permits",
               "Replenish in"));
       stdout.println(DASHED_LINE);
       uploadPackPerHour.asMap().entrySet().stream()
@@ -75,7 +69,6 @@
       throw die(e);
     }
   }
-
   private void printEntry(Entry<String, RateLimiter> entry) {
     stdout.println(
         String.format(
@@ -83,13 +76,12 @@
             getDisplayValue(entry.getKey()),
             permits(entry.getValue().permitsPerHour()),
             permits(entry.getValue().availablePermits()),
+            permits(entry.getValue().usedPermits()),
             Duration.ofSeconds(entry.getValue().remainingTime(TimeUnit.SECONDS))));
   }
-
   private String permits(int value) {
     return value == Integer.MAX_VALUE ? "unlimited" : Integer.toString(value);
   }
-
   private String getDisplayValue(String key) {
     Optional<String> currentUser = userResolver.getUserName(key);
     return currentUser.map(name -> key + " (" + name + ")").orElse(key);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java
index c59c2fe..a856772 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java
@@ -36,6 +36,7 @@
 
 class Module extends AbstractModule {
   static final String UPLOAD_PACK_PER_HOUR = "upload_pack_per_hour";
+  static final String DEFAULT_RATE_LIMIT_TYPE = "upload pack";
 
   @Override
   protected void configure() {
@@ -96,10 +97,13 @@
         return UnlimitedRateLimiter.INSTANCE;
       }
 
+      String rateLimitType = DEFAULT_RATE_LIMIT_TYPE;
+
       // In the case that there is a warning but no limit
       Integer myLimit = Integer.MAX_VALUE;
       if (limit.isPresent()) {
         myLimit = limit.get().getRatePerHour();
+        rateLimitType = limit.get().getType().getLimitType();
       }
 
       long effectiveTimeLapse = PeriodicRateLimiter.DEFAULT_TIME_LAPSE_IN_MINUTES;
@@ -107,13 +111,15 @@
         long providedTimeLapse = timeLapse.get().getRatePerHour();
         if (providedTimeLapse > 0 && providedTimeLapse <= effectiveTimeLapse) {
           effectiveTimeLapse = providedTimeLapse;
+          rateLimitType = timeLapse.get().getType().getLimitType();
         } else {
           logger.warn(
               "The time lapse is set to the default {} minutes, as the configured value is invalid.",
               effectiveTimeLapse);
         }
       }
-      RateLimiter rateLimiter = periodicRateLimiterFactory.create(myLimit, effectiveTimeLapse);
+      RateLimiter rateLimiter =
+          periodicRateLimiterFactory.create(myLimit, effectiveTimeLapse, rateLimitType);
 
       if (warn.isPresent()) {
         if (limit.isPresent()) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java
index cb86336..5c5ced3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java
@@ -29,19 +29,22 @@
   private final int maxPermits;
   private final AtomicInteger usedPermits;
   private final ScheduledFuture<?> replenishTask;
+  private final String rateLimitType;
 
   interface Factory {
-    PeriodicRateLimiter create(int permits, long timeLapse);
+    PeriodicRateLimiter create(int permits, long timeLapse, String rateLimitType);
   }
 
   @Inject
   PeriodicRateLimiter(
       @RateLimitExecutor ScheduledExecutorService executor,
       @Assisted int permits,
-      @Assisted long timeLapse) {
+      @Assisted long timeLapse,
+      @Assisted String rateLimitType) {
     this.semaphore = new Semaphore(permits);
     this.maxPermits = permits;
     this.usedPermits = new AtomicInteger();
+    this.rateLimitType = rateLimitType;
     this.replenishTask =
         executor.scheduleAtFixedRate(
             this::replenishPermits, timeLapse, timeLapse, TimeUnit.MINUTES);
@@ -83,6 +86,11 @@
   }
 
   @Override
+  public String getType() {
+    return rateLimitType;
+  }
+
+  @Override
   public void close() {
     replenishTask.cancel(true);
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java
index 337508f..0d32776 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java
@@ -15,14 +15,16 @@
 package com.googlesource.gerrit.plugins.ratelimiter;
 
 enum RateLimitType {
-  UPLOAD_PACK_PER_HOUR("uploadpackperhour"),
-  UPLOAD_PACK_PER_HOUR_WARN("uploadpackperhourwarn"),
-  TIME_LAPSE_IN_MINUTES("timelapseinminutes");
+  UPLOAD_PACK_PER_HOUR("uploadpackperhour", "upload pack"),
+  UPLOAD_PACK_PER_HOUR_WARN("uploadpackperhourwarn", "upload pack"),
+  TIME_LAPSE_IN_MINUTES("timelapseinminutes", "upload pack");
 
   private final String type;
+  private final String limitType;
 
-  RateLimitType(String type) {
+  RateLimitType(String type, String limitType) {
     this.type = type;
+    this.limitType = limitType;
   }
 
   @Override
@@ -30,6 +32,10 @@
     return type;
   }
 
+  public String getLimitType() {
+    return limitType;
+  }
+
   static RateLimitType from(String value) {
     for (RateLimitType rateLimitType : RateLimitType.values()) {
       if (rateLimitType.toString().equalsIgnoreCase(value)) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimiter.java
index 7c695de..d5455b1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimiter.java
@@ -46,6 +46,9 @@
   /** Replenish available permits to the number allowed per hour. */
   void replenishPermits();
 
+  /** Return type of rate limiter * */
+  String getType();
+
   /** Closes this RateLimiter, relinquishing any underlying resources. */
   void close();
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/UnlimitedRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/UnlimitedRateLimiter.java
index a3e682e..d1989f4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/UnlimitedRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/UnlimitedRateLimiter.java
@@ -14,6 +14,8 @@
 
 package com.googlesource.gerrit.plugins.ratelimiter;
 
+import static com.googlesource.gerrit.plugins.ratelimiter.Module.DEFAULT_RATE_LIMIT_TYPE;
+
 import java.util.concurrent.TimeUnit;
 
 class UnlimitedRateLimiter implements RateLimiter {
@@ -48,6 +50,11 @@
   }
 
   @Override
+  public String getType() {
+    return DEFAULT_RATE_LIMIT_TYPE;
+  }
+
+  @Override
   public int usedPermits() {
     return 0;
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java
index 5ce2200..317cd26 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java
@@ -64,19 +64,21 @@
     boolean acquirePermit = delegate.acquirePermit();
     if (usedPermits() == warnLimit) {
       rateLimitLog.info(
-          "{} reached the warning limit of {} uploadpacks per {} minutes.",
+          "{} reached the warning limit of {} {} per {} minutes.",
           userResolver.getUserName(key).orElse(key),
           warnLimit,
+          delegate.getType(),
           timeLapse);
       warningWasLogged = true;
     }
 
     if (!acquirePermit && !wasLogged) {
       rateLimitLog.info(
-          "{} was blocked due to exceeding the limit of {} uploadpacks per {} minutes."
+          "{} was blocked due to exceeding the limit of {} {} per {} minutes."
               + " {} remaining to permits replenishing.",
           userResolver.getUserName(key).orElse(key),
           permitsPerHour(),
+          delegate.getType(),
           timeLapse,
           secondsToMsSs(remainingTime(TimeUnit.SECONDS)));
       wasLogged = true;
@@ -101,6 +103,11 @@
   }
 
   @Override
+  public String getType() {
+    return delegate.getType();
+  }
+
+  @Override
   public void close() {
     delegate.close();
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java
index f9e55b3..7ce5be1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java
@@ -61,9 +61,10 @@
 
     if (acquirePermit && (usedPermits() == warnLimit)) {
       rateLimitLog.info(
-          "{} reached the warning limit of {} uploadpacks per {} minutes.",
+          "{} reached the warning limit of {} {} per {} minutes.",
           userResolver.getUserName(key).orElse(key),
           warnLimit,
+          delegate.getType(),
           timeLapse);
       warningWasLogged = true;
     }
@@ -87,6 +88,11 @@
   }
 
   @Override
+  public String getType() {
+    return delegate.getType();
+  }
+
+  @Override
   public int usedPermits() {
     return delegate.usedPermits();
   }
diff --git a/src/main/resources/Documentation/cmd-list.md b/src/main/resources/Documentation/cmd-list.md
index d541cfa..c5116a7 100644
--- a/src/main/resources/Documentation/cmd-list.md
+++ b/src/main/resources/Documentation/cmd-list.md
@@ -1,36 +1,16 @@
-@PLUGIN@ list
-=================
-
-NAME
-----
-@PLUGIN@ list display rate limit statistics
-
-SYNOPSIS
---------
->     ssh -p <port> <host> @PLUGIN@ list
-
-DESCRIPTION
------------
-Displays rate limit statistics: account id (or IP if request is anonymous),
-permits per hour, remaining permits and when they will be replenished.
-
-The time before permits are replenished is represented using ISO-8601 seconds
 based representation, such as PT59M30S (59 minutes and 30 seconds).
-
 ACCESS
 ------
 Gerrit Administrators only.
-
 EXAMPLES
 --------
-
 >     $ ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ list
->     ------------------------------------------------------------------------------
+>     ---------------------------------------------------------------------------------------------
 >     * upload_pack_per_hour *
->     ------------------------------------------------------------------------------
->     Account Id (or IP)   Permits Per Hour  Available Permits   Replenish in
->     ------------------------------------------------------------------------------
->     1000001              unlimited         unlimited           PT0S
->     1000002              1000              999                 PT59M30S
->     127.0.0.1            1000              123                 PT10M26S
->     ------------------------------------------------------------------------------
+>     ---------------------------------------------------------------------------------------------
+>     Account Id/IP (username)   Permits Per Hour  Available Permits   Used Permits    Replenish in
+>     ---------------------------------------------------------------------------------------------
+>     1000000 (admin)            unlimited         unlimited           0               PT0S
+>     1000001 (test_user)        1000              999                 1               PT59M30S
+>     127.0.0.1                  1000              123                 877             PT10M26S
+>     ---------------------------------------------------------------------------------------------
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java
index 1cb3764..b949490 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java
@@ -37,7 +37,9 @@
   @Before
   public void setUp() {
     scheduledExecutorMock = mock(ScheduledExecutorService.class);
-    limiter = new PeriodicRateLimiter(scheduledExecutorMock, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
+    limiter =
+        new PeriodicRateLimiter(
+            scheduledExecutorMock, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES, "Any Type");
   }
 
   @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java
index 0353a92..9b41949 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java
@@ -45,11 +45,15 @@
     ScheduledExecutorService scheduledExecutorMock2 = mock(ScheduledExecutorService.class);
 
     PeriodicRateLimiter limiter1 =
-        spy(new PeriodicRateLimiter(scheduledExecutorMock1, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES));
+        spy(
+            new PeriodicRateLimiter(
+                scheduledExecutorMock1, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES, "Any Type"));
     doReturn(1L).when(limiter1).remainingTime(any(TimeUnit.class));
 
     PeriodicRateLimiter limiter2 =
-        spy(new PeriodicRateLimiter(scheduledExecutorMock2, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES));
+        spy(
+            new PeriodicRateLimiter(
+                scheduledExecutorMock2, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES, "Any Type"));
     doReturn(1L).when(limiter2).remainingTime(any(TimeUnit.class));
 
     warningLimiter1 =
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java
index facabbe..7717234 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java
@@ -39,7 +39,8 @@
   public void setUp() {
     scheduledExecutorMock = mock(ScheduledExecutorService.class);
     PeriodicRateLimiter limiter =
-        new PeriodicRateLimiter(scheduledExecutorMock, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
+        new PeriodicRateLimiter(
+            scheduledExecutorMock, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES, "Any Type");
     warningUnlimitedLimiter =
         new WarningUnlimitedRateLimiter(
             userResolver, limiter, "dummy", WARN_RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);