Repository navigation
Target Group Quota introduction #3337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ | |
| import java.util.Objects; | ||
| import java.util.Optional; | ||
| import java.util.Set; | ||
| import java.util.SortedSet; | ||
| import java.util.TreeSet; | ||
| import java.util.function.BiFunction; | ||
| import java.util.function.Consumer; | ||
| import java.util.function.Function; | ||
|
|
@@ -34,6 +36,7 @@ | |
| import jakarta.validation.constraints.NotEmpty; | ||
|
|
||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.eclipse.hawkbit.context.AccessContext; | ||
| import org.eclipse.hawkbit.ql.jpa.QLSupport; | ||
| import org.eclipse.hawkbit.repository.QuotaManagement; | ||
| import org.eclipse.hawkbit.repository.TargetManagement; | ||
|
|
@@ -102,6 +105,50 @@ protected JpaTargetManagement( | |
| this.targetTagRepository = targetTagRepository; | ||
| } | ||
|
|
||
| @Override | ||
| @Transactional | ||
| @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY) | ||
| public JpaTarget create(final TargetManagement.Create create) { | ||
| assertTargetGroupQuota(Collections.singletonList(create.getGroup())); | ||
| return super.create(create); | ||
| } | ||
|
|
||
| @Override | ||
| @Transactional | ||
| @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY) | ||
| public List<JpaTarget> create(final Collection<TargetManagement.Create> create) { | ||
| assertTargetGroupQuota(create.stream().map(TargetManagement.Create::getGroup).toList()); | ||
| return super.create(create); | ||
| } | ||
|
|
||
| @Override | ||
| @Transactional | ||
| @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY) | ||
| public JpaTarget update(final TargetManagement.Update update) { | ||
| try { | ||
| assertTargetGroupQuota(Collections.singletonList(update.getGroup())); | ||
| } catch (final Exception ex) { | ||
| // target existence check in order to throw EntityNotFound instead of AssignmentQuotaException if both applicable | ||
| getValid(update.getId()); | ||
| throw ex; | ||
| } | ||
| return super.update(update); | ||
| } | ||
|
|
||
| @Override | ||
| @Transactional | ||
| @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY) | ||
| public Map<Long, JpaTarget> update(final Collection<TargetManagement.Update> update) { | ||
| try { | ||
| assertTargetGroupQuota(update.stream().map(TargetManagement.Update::getGroup).toList()); | ||
| } catch (final Exception ex) { | ||
| // target existence check in order to throw EntityNotFound instead of AssignmentQuotaException if both applicable | ||
| get(update.stream().map(TargetManagement.Update::getId).toList()); | ||
| throw ex; | ||
| } | ||
| return super.update(update); | ||
| } | ||
|
|
||
| @Override | ||
| public Map<String, String> getControllerAttributes(final String controllerId) { | ||
| return getMap(controllerId, JpaTarget_.controllerAttributes); | ||
|
|
@@ -339,6 +386,8 @@ public Target unassignType(final String controllerId) { | |
| @Transactional | ||
| @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY) | ||
| public void assignTargetGroupWithRsql(String group, String rsql) { | ||
| // Quota check | ||
| assertTargetGroupQuota(Collections.singletonList(group)); | ||
|
|
||
| // Switch back to UpdateAllQuery if switching back to hibernate. (EclipseLink does not work well with UpdateAllQuery) | ||
| // EclipseLink: using subquery approach — applying predicate directly to the UPDATE root | ||
|
|
@@ -401,6 +450,9 @@ private void assignTargetGroupOnChunks(final String group, final String rsql) { | |
| @Transactional | ||
| @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY) | ||
| public void assignTargetsWithGroup(String group, List<String> controllerIds) { | ||
| // Quota check | ||
| assertTargetGroupQuota(Collections.singletonList(group)); | ||
|
|
||
| final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); | ||
| CriteriaUpdate<JpaTarget> criteriaQuery = cb.createCriteriaUpdate(JpaTarget.class); | ||
| Root<JpaTarget> root = criteriaQuery.from(JpaTarget.class); | ||
|
|
@@ -552,4 +604,34 @@ private void throwEntityNotFoundExceptionIfTagDoesNotExist(final Long tagId) { | |
| throw new EntityNotFoundException(TargetTag.class, tagId); | ||
| } | ||
| } | ||
|
|
||
| private void assertTargetGroupQuota(final Collection<String> requested) { | ||
| final SortedSet<String> wanted = requested.stream() | ||
| .filter(Objects::nonNull) | ||
| .collect(Collectors.toCollection(TreeSet::new)); | ||
|
|
||
| if (wanted.isEmpty()) { | ||
| return; // no group(s), skip findDistinctGroups db call | ||
| } | ||
|
|
||
| final long limit = quotaManagement.getMaxTargetGroups(); | ||
| if (limit <= 0) { | ||
| return; | ||
| } | ||
|
|
||
| // one group, already present -> allowed | ||
| if (wanted.size() == 1 && jpaRepository.existsByGroup(wanted.first())) { | ||
| return; | ||
| } | ||
| final List<String> existing = jpaRepository.findDistinctGroups(AccessContext.tenant()); | ||
|
strailov marked this conversation as resolved.
|
||
|
|
||
| existing.forEach(wanted::remove); | ||
| if (wanted.isEmpty()) { | ||
| return; // no growth -> allowed | ||
| } | ||
|
|
||
| QuotaHelper.assertAssignmentQuota( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we won't get into : |
||
| AccessContext.tenant(), wanted.size(), limit, "target group", "tenant", | ||
| tenant -> existing.size()); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.