You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by lf...@apache.org on 2020/07/20 13:04:42 UTC
[incubator-dlab] 01/02: [DLAB-1714] BE created notification in case
project quota is exceeded
This is an automated email from the ASF dual-hosted git repository.
lfrolov pushed a commit to branch DLAB-1944
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit 5b2fe0395fa9e81e8133e3e65f5d918493a35489
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Fri Jul 17 18:05:47 2020 +0300
[DLAB-1714] BE created notification in case project quota is exceeded
---
.../epam/dlab/backendapi/dao/BaseBillingDAO.java | 28 +------------
.../com/epam/dlab/backendapi/dao/BillingDAO.java | 8 +---
.../interceptor/BudgetLimitInterceptor.java | 5 ++-
.../dlab/backendapi/resources/BillingResource.java | 8 ++++
.../resources/dto/HealthStatusPageDTO.java | 4 --
.../dto/QuotaUsageDTO.java} | 22 ++++------
.../schedulers/CheckProjectQuoteScheduler.java | 6 +--
.../dlab/backendapi/service/BillingService.java | 15 +++++--
.../service/impl/BillingServiceImpl.java | 48 +++++++++++++++++++++-
.../impl/InfrastructureInfoServiceImpl.java | 11 ++---
10 files changed, 89 insertions(+), 66 deletions(-)
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java
index 0e21bba..7975eeb 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java
@@ -20,10 +20,8 @@
package com.epam.dlab.backendapi.dao;
import com.epam.dlab.backendapi.domain.BillingReportLine;
-import com.epam.dlab.backendapi.domain.BudgetDTO;
import com.epam.dlab.backendapi.resources.dto.BillingFilter;
import com.epam.dlab.dto.billing.BillingResourceType;
-import com.epam.dlab.exceptions.ResourceNotFoundException;
import com.google.inject.Inject;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
@@ -101,14 +99,14 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
}
@Override
- public Double getProjectCost(String project) {
+ public Double getOverallProjectCost(String project) {
final List<Bson> pipeline = Arrays.asList(match(eq(PROJECT, project)),
group(null, sum(TOTAL_FIELD_NAME, COST_FIELD)));
return aggregateBillingData(pipeline);
}
@Override
- public Double getProjectCost(String project, LocalDate date) {
+ public Double getMonthlyProjectCost(String project, LocalDate date) {
final List<Bson> pipeline = Arrays.asList(match(
and(
eq(PROJECT, project),
@@ -143,28 +141,10 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
}
@Override
- public boolean isProjectQuoteReached(String project) {
- final boolean monthlyBudget = Optional.ofNullable(projectDAO.get(project)
- .orElseThrow(projectNotFound())
- .getBudget())
- .map(BudgetDTO::isMonthlyBudget)
- .orElse(Boolean.FALSE);
- final Double projectCost = monthlyBudget ? getProjectCost(project, LocalDate.now()) : getProjectCost(project);
- return projectDAO.getAllowedBudget(project)
- .filter(allowedBudget -> projectCost.intValue() != 0 && allowedBudget <= projectCost)
- .isPresent();
- }
-
- @Override
public List<BillingReportLine> findBillingData(String project, String endpoint, List<String> resourceNames) {
return find(BILLING, and(eq(PROJECT, project), eq(ENDPOINT, endpoint), in(RESOURCE_NAME, resourceNames)), BillingReportLine.class);
}
- @Override
- public int getBillingProjectQuoteUsed(String project) {
- return toPercentage(() -> projectDAO.getAllowedBudget(project), getProjectCost(project));
- }
-
public List<BillingReportLine> aggregateBillingData(BillingFilter filter) {
List<Bson> pipeline = new ArrayList<>();
List<Bson> matchCriteria = matchCriteria(filter);
@@ -262,8 +242,4 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
.currency(id.getString(CURRENCY))
.build();
}
-
- private Supplier<ResourceNotFoundException> projectNotFound() {
- return () -> new ResourceNotFoundException("Project with passed name not found");
- }
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java
index da1c9a7..088c319 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java
@@ -29,22 +29,18 @@ public interface BillingDAO {
Double getUserCost(String user);
- Double getProjectCost(String project);
+ Double getOverallProjectCost(String project);
- Double getProjectCost(String project, LocalDate date);
+ Double getMonthlyProjectCost(String project, LocalDate date);
int getBillingQuoteUsed();
int getBillingUserQuoteUsed(String user);
- int getBillingProjectQuoteUsed(String project);
-
boolean isBillingQuoteReached();
boolean isUserQuoteReached(String user);
- boolean isProjectQuoteReached(String project);
-
List<BillingReportLine> findBillingData(String project, String endpoint, List<String> resourceNames);
List<BillingReportLine> aggregateBillingData(BillingFilter filter);
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java
index 43fdaf6..ce32e10 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java
@@ -22,6 +22,7 @@ package com.epam.dlab.backendapi.interceptor;
import com.epam.dlab.auth.UserInfo;
import com.epam.dlab.backendapi.annotation.Project;
import com.epam.dlab.backendapi.dao.BillingDAO;
+import com.epam.dlab.backendapi.service.BillingService;
import com.epam.dlab.exceptions.ResourceQuoteReachedException;
import com.google.inject.Inject;
import lombok.extern.slf4j.Slf4j;
@@ -38,6 +39,8 @@ import java.util.stream.IntStream;
public class BudgetLimitInterceptor implements MethodInterceptor {
@Inject
private BillingDAO billingDAO;
+ @Inject
+ private BillingService billingService;
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
@@ -66,7 +69,7 @@ public class BudgetLimitInterceptor implements MethodInterceptor {
.filter(i -> Objects.nonNull(parameters[i].getAnnotation(Project.class)))
.mapToObj(i -> (String) mi.getArguments()[i])
.findAny()
- .map(billingDAO::isProjectQuoteReached)
+ .map(billingService::isProjectQuoteReached)
.orElse(Boolean.FALSE);
}
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
index 1916a38..a03edb3 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
@@ -28,6 +28,7 @@ import io.dropwizard.auth.Auth;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@@ -46,6 +47,13 @@ public class BillingResource {
this.billingService = billingService;
}
+ @GET
+ @Path("/quota")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getQuota(@Auth UserInfo userInfo) {
+ return Response.ok(billingService.getQuotas(userInfo)).build();
+ }
+
@POST
@Path("/report")
@Produces(MediaType.APPLICATION_JSON)
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
index 642dd06..73fd421 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
@@ -44,10 +44,6 @@ public class HealthStatusPageDTO {
@JsonProperty
private boolean projectAdmin;
@JsonProperty
- private int billingQuoteUsed;
- @JsonProperty
- private int billingUserQuoteUsed;
- @JsonProperty
private boolean projectAssigned;
@JsonProperty
private BucketBrowser bucketBrowser;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/QuotaUsageDTO.java
similarity index 58%
copy from services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
copy to services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/QuotaUsageDTO.java
index b76b141..e611091 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/QuotaUsageDTO.java
@@ -17,20 +17,16 @@
* under the License.
*/
-package com.epam.dlab.backendapi.service;
+package com.epam.dlab.backendapi.resources.dto;
-import com.epam.dlab.auth.UserInfo;
-import com.epam.dlab.backendapi.domain.BillingReport;
-import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import lombok.Builder;
+import lombok.Data;
-import java.util.List;
+import java.util.Map;
-public interface BillingService {
- BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
-
- String downloadReport(UserInfo userInfo, BillingFilter filter);
-
- BillingReport getExploratoryBillingData(String project, String endpoint, String exploratoryName, List<String> compNames);
-
- void updateRemoteBillingData(UserInfo userInfo);
+@Data
+@Builder
+public class QuotaUsageDTO {
+ private int totalQuotaUsed;
+ private Map<String, Integer> projectQuotas;
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java
index 030c10d..2dc499f 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java
@@ -19,9 +19,9 @@
package com.epam.dlab.backendapi.schedulers;
-import com.epam.dlab.backendapi.dao.BillingDAO;
import com.epam.dlab.backendapi.domain.ProjectDTO;
import com.epam.dlab.backendapi.schedulers.internal.Scheduled;
+import com.epam.dlab.backendapi.service.BillingService;
import com.epam.dlab.backendapi.service.EnvironmentService;
import com.epam.dlab.backendapi.service.ProjectService;
import com.google.inject.Inject;
@@ -34,7 +34,7 @@ import org.quartz.JobExecutionContext;
public class CheckProjectQuoteScheduler implements Job {
@Inject
- private BillingDAO billingDAO;
+ private BillingService billingService;
@Inject
private EnvironmentService environmentService;
@Inject
@@ -45,7 +45,7 @@ public class CheckProjectQuoteScheduler implements Job {
projectService.getProjects()
.stream()
.map(ProjectDTO::getName)
- .filter(billingDAO::isProjectQuoteReached)
+ .filter(billingService::isProjectQuoteReached)
.peek(p -> log.debug("Stopping {} project env because of reaching user billing quote", p))
.forEach(environmentService::stopProjectEnvironment);
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
index b76b141..7291cc7 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
@@ -22,15 +22,22 @@ package com.epam.dlab.backendapi.service;
import com.epam.dlab.auth.UserInfo;
import com.epam.dlab.backendapi.domain.BillingReport;
import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import com.epam.dlab.backendapi.resources.dto.QuotaUsageDTO;
import java.util.List;
public interface BillingService {
- BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
+ BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
- String downloadReport(UserInfo userInfo, BillingFilter filter);
+ String downloadReport(UserInfo userInfo, BillingFilter filter);
- BillingReport getExploratoryBillingData(String project, String endpoint, String exploratoryName, List<String> compNames);
+ BillingReport getExploratoryBillingData(String project, String endpoint, String exploratoryName, List<String> compNames);
- void updateRemoteBillingData(UserInfo userInfo);
+ void updateRemoteBillingData(UserInfo userInfo);
+
+ QuotaUsageDTO getQuotas(UserInfo userInfo);
+
+ boolean isProjectQuoteReached(String project);
+
+ int getBillingProjectQuoteUsed(String project);
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java
index c0b9aa2..2bcf644 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java
@@ -23,12 +23,15 @@ import com.epam.dlab.auth.UserInfo;
import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
import com.epam.dlab.backendapi.dao.BillingDAO;
import com.epam.dlab.backendapi.dao.ImageExploratoryDao;
+import com.epam.dlab.backendapi.dao.ProjectDAO;
import com.epam.dlab.backendapi.domain.BillingReport;
import com.epam.dlab.backendapi.domain.BillingReportLine;
+import com.epam.dlab.backendapi.domain.BudgetDTO;
import com.epam.dlab.backendapi.domain.EndpointDTO;
import com.epam.dlab.backendapi.domain.ProjectDTO;
import com.epam.dlab.backendapi.domain.ProjectEndpointDTO;
import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import com.epam.dlab.backendapi.resources.dto.QuotaUsageDTO;
import com.epam.dlab.backendapi.roles.RoleType;
import com.epam.dlab.backendapi.roles.UserRoles;
import com.epam.dlab.backendapi.service.BillingService;
@@ -63,7 +66,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -73,6 +78,7 @@ public class BillingServiceImpl implements BillingService {
private static final String USAGE_DATE_FORMAT = "yyyy-MM";
private final ProjectService projectService;
+ private final ProjectDAO projectDAO;
private final EndpointService endpointService;
private final ExploratoryService exploratoryService;
private final SelfServiceApplicationConfiguration configuration;
@@ -82,11 +88,12 @@ public class BillingServiceImpl implements BillingService {
private final String sbn;
@Inject
- public BillingServiceImpl(ProjectService projectService, EndpointService endpointService,
+ public BillingServiceImpl(ProjectService projectService, ProjectDAO projectDAO, EndpointService endpointService,
ExploratoryService exploratoryService, SelfServiceApplicationConfiguration configuration,
@Named(ServiceConsts.BILLING_SERVICE_NAME) RESTService provisioningService, ImageExploratoryDao imageExploratoryDao,
BillingDAO billingDAO) {
this.projectService = projectService;
+ this.projectDAO = projectDAO;
this.endpointService = endpointService;
this.exploratoryService = exploratoryService;
this.configuration = configuration;
@@ -176,6 +183,38 @@ public class BillingServiceImpl implements BillingService {
});
}
+ @Override
+ public QuotaUsageDTO getQuotas(UserInfo userInfo) {
+ int totalQuota = billingDAO.getBillingQuoteUsed();
+ Map<String, Integer> projectQuotas = projectService.getProjects(userInfo)
+ .stream()
+ .collect(Collectors.toMap(ProjectDTO::getName, p -> getBillingProjectQuoteUsed(p.getName())));
+ return QuotaUsageDTO.builder()
+ .totalQuotaUsed(totalQuota)
+ .projectQuotas(projectQuotas)
+ .build();
+ }
+
+ @Override
+ public boolean isProjectQuoteReached(String project) {
+ final Double projectCost = getProjectCost(project);
+ return projectDAO.getAllowedBudget(project)
+ .filter(allowedBudget -> projectCost.intValue() != 0 && allowedBudget <= projectCost)
+ .isPresent();
+ }
+
+ @Override
+ public int getBillingProjectQuoteUsed(String project) {
+ return toPercentage(() -> projectDAO.getAllowedBudget(project), getProjectCost(project));
+ }
+
+ private Double getProjectCost(String project) {
+ final boolean monthlyBudget = Optional.ofNullable(projectService.get(project).getBudget())
+ .map(BudgetDTO::isMonthlyBudget)
+ .orElse(Boolean.FALSE);
+ return monthlyBudget ? billingDAO.getMonthlyProjectCost(project, LocalDate.now()) : billingDAO.getOverallProjectCost(project);
+ }
+
private Map<String, BillingReportLine> getBillableResources() {
Set<ProjectDTO> projects = new HashSet<>(projectService.getProjects());
final Stream<BillingReportLine> ssnBillingDataStream = BillingUtils.ssnBillingDataStream(sbn);
@@ -341,4 +380,11 @@ public class BillingServiceImpl implements BillingService {
.exploratoryName(billingReportLine.getExploratoryName())
.build();
}
+
+ private Integer toPercentage(Supplier<Optional<Integer>> allowedBudget, Double totalCost) {
+ return allowedBudget.get()
+ .map(userBudget -> (totalCost * 100) / userBudget)
+ .map(Double::intValue)
+ .orElse(BigDecimal.ZERO.intValue());
+ }
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
index 475f2db..98c2f6d 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
@@ -21,7 +21,6 @@ package com.epam.dlab.backendapi.service.impl;
import com.epam.dlab.auth.UserInfo;
import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
-import com.epam.dlab.backendapi.dao.BillingDAO;
import com.epam.dlab.backendapi.dao.ExploratoryDAO;
import com.epam.dlab.backendapi.domain.BillingReport;
import com.epam.dlab.backendapi.domain.EndpointDTO;
@@ -65,18 +64,16 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
private final ExploratoryDAO expDAO;
private final SelfServiceApplicationConfiguration configuration;
- private final BillingDAO billingDAO;
private final ProjectService projectService;
private final EndpointService endpointService;
private final BillingService billingService;
@Inject
public InfrastructureInfoServiceImpl(ExploratoryDAO expDAO, SelfServiceApplicationConfiguration configuration,
- BillingDAO billingDAO, ProjectService projectService, EndpointService endpointService,
- BillingService billingService) {
+ ProjectService projectService, EndpointService endpointService,
+ BillingService billingService) {
this.expDAO = expDAO;
this.configuration = configuration;
- this.billingDAO = billingDAO;
this.projectService = projectService;
this.endpointService = endpointService;
this.billingService = billingService;
@@ -91,7 +88,7 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
.stream()
.map(p -> {
List<UserInstanceDTO> exploratories = expDAO.findExploratories(user.getName(), p.getName());
- return new ProjectInfrastructureInfo(p.getName(), billingDAO.getBillingProjectQuoteUsed(p.getName()),
+ return new ProjectInfrastructureInfo(p.getName(), billingService.getBillingProjectQuoteUsed(p.getName()),
getSharedInfo(p.getName()), exploratories, getExploratoryBillingData(exploratories),
getEndpoints(allEndpoints, p));
})
@@ -115,8 +112,6 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
.projectAdmin(UserRoles.isProjectAdmin(userInfo))
.admin(UserRoles.isAdmin(userInfo))
.projectAssigned(projectService.isAnyProjectAssigned(userInfo))
- .billingQuoteUsed(billingDAO.getBillingQuoteUsed())
- .billingUserQuoteUsed(billingDAO.getBillingUserQuoteUsed(user))
.bucketBrowser(HealthStatusPageDTO.BucketBrowser.builder()
.view(checkAccess(userInfo, PERMISSION_VIEW))
.upload(checkAccess(userInfo, PERMISSION_UPLOAD))
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org