Line data Source code
1 : // Copyright (C) 2018 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.quota; 16 : 17 : import com.google.gerrit.entities.Account; 18 : import com.google.gerrit.entities.Change; 19 : import com.google.gerrit.entities.Project; 20 : import com.google.gerrit.server.CurrentUser; 21 : import com.google.inject.ImplementedBy; 22 : 23 : /** 24 : * Backend interface to perform quota requests on. By default, this interface is backed by {@link 25 : * DefaultQuotaBackend} which calls all plugins that implement {@link QuotaEnforcer}. A different 26 : * implementation might be bound in tests. Plugins are not supposed to implement this interface, but 27 : * bind a {@link QuotaEnforcer} implementation instead. 28 : * 29 : * <p>All quota requests require a quota group and a user. Enriching them with a top-level entity 30 : * {@code Change, Project, Account} is optional but should be done if the request is targeted. 31 : * 32 : * <p>Example usage: 33 : * 34 : * <pre> 35 : * quotaBackend.currentUser().project(projectName).requestToken("/projects/create").throwOnError(); 36 : * quotaBackend.user(user).requestToken("/restapi/config/put").throwOnError(); 37 : * QuotaResponse.Aggregated result = quotaBackend.currentUser().account(accountId).requestToken("/restapi/accounts/emails/validate"); 38 : * QuotaResponse.Aggregated result = quotaBackend.currentUser().project(projectName).requestTokens("/projects/git/upload", numBytesInPush); 39 : * </pre> 40 : * 41 : * <p>All quota groups must be documented in {@code quota.txt} and detail the metadata that is 42 : * provided (i.e. the parameters used to scope down the quota request). 43 : */ 44 : @ImplementedBy(DefaultQuotaBackend.class) 45 : public interface QuotaBackend { 46 : /** Constructs a request for the current user. */ 47 : WithUser currentUser(); 48 : 49 : /** 50 : * See {@link #currentUser()}. Use this method only if you can't guarantee that the request is for 51 : * the current user (e.g. impersonation). 52 : */ 53 : WithUser user(CurrentUser user); 54 : 55 : /** 56 : * An interface capable of issuing quota requests. Scope can be futher reduced by providing a 57 : * top-level entity. 58 : */ 59 : interface WithUser extends WithResource { 60 : /** Scope the request down to an account. */ 61 : WithResource account(Account.Id account); 62 : 63 : /** Scope the request down to a project. */ 64 : WithResource project(Project.NameKey project); 65 : 66 : /** Scope the request down to a change. */ 67 : WithResource change(Change.Id change, Project.NameKey project); 68 : } 69 : 70 : /** An interface capable of issuing quota requests. */ 71 : interface WithResource { 72 : /** Issues a single quota request for {@code 1} token. */ 73 : default QuotaResponse.Aggregated requestToken(String quotaGroup) { 74 28 : return requestTokens(quotaGroup, 1); 75 : } 76 : 77 : /** Issues a single quota request for {@code numTokens} tokens. */ 78 : QuotaResponse.Aggregated requestTokens(String quotaGroup, long numTokens); 79 : 80 : /** 81 : * Issues a single quota request for {@code numTokens} tokens but signals the implementations 82 : * not to deduct any quota yet. Can be used to do pre-flight requests where necessary 83 : */ 84 : QuotaResponse.Aggregated dryRun(String quotaGroup, long tokens); 85 : 86 : /** 87 : * Requests a minimum number of tokens available in implementations. This is a pre-flight check 88 : * for the exceptional case when the requested number of tokens is not known in advance but 89 : * boundary can be specified. For instance, when the commit is received its size is not known 90 : * until the transfer happens however one can specify how many bytes can be accepted to meet the 91 : * repository size quota. 92 : * 93 : * <p>By definition, this is not an allocating request, therefore, it should be followed by the 94 : * call to {@link #requestTokens(String, long)} when the size gets determined so that quota 95 : * could be properly adjusted. It is in developer discretion to ensure that it gets called. 96 : * There might be a case when particular quota gets temporarily overbooked when multiple 97 : * requests are performed but the following calls to {@link #requestTokens(String, long)} will 98 : * fail at the moment when a quota is exhausted. It is not a subject of quota backend to reclaim 99 : * tokens that were used due to overbooking. 100 : */ 101 : QuotaResponse.Aggregated availableTokens(String quotaGroup); 102 : } 103 : }