Skip to content
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

added triggeredBy for rollouts and autoassignment #1017

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public interface TenantAware {
*/
String getCurrentTenant();

/**
* @return the username of the currently logged-in user
*/
String getCurrentUsername();

/**
* Gives the possibility to run a certain code under a specific given
* {@code tenant}. Only the given {@link TenantRunner} is executed under the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,45 @@ public interface DeploymentManagement {
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List<DistributionSetAssignmentResult> assignDistributionSets(
List<DistributionSetAssignmentResult> assignDistributionSets(@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);

/**
* Assigns {@link DistributionSet}s to {@link Target}s according to the
* {@link DeploymentRequest}.
*
* @param triggeredBy
* the username of the user who triggered the assignment
* @param deploymentRequests
* information about all target-ds-assignments that shall be made
*
* @return the list of assignment results
*
* @throws IncompleteDistributionSetException
* if mandatory {@link SoftwareModuleType} are not assigned as
* defined by the {@link DistributionSetType}.
*
* @throws EntityNotFoundException
* if either provided {@link DistributionSet} or {@link Target}s
* do not exist
*
* @throws AssignmentQuotaExceededException
* if the maximum number of targets the distribution set can be
* assigned to at once is exceeded
* @throws MultiAssignmentIsNotEnabledException
* if the request results in multiple assignments to the same
* target and multiassignment is disabled
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List<DistributionSetAssignmentResult> assignDistributionSets(String triggeredBy,
@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);

/**
* Assigns {@link DistributionSet}s to {@link Target}s according to the
* {@link DeploymentRequest}.
*
* @param triggeredBy
* the username of the user who triggered the assignment
* @param deploymentRequests
* information about all target-ds-assignments that shall be made
* @param actionMessage
Expand All @@ -107,7 +139,7 @@ List<DistributionSetAssignmentResult> assignDistributionSets(
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List<DistributionSetAssignmentResult> assignDistributionSets(
List<DistributionSetAssignmentResult> assignDistributionSets(String triggeredBy,
@Valid @NotEmpty List<DeploymentRequest> deploymentRequests, String actionMessage);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ default boolean isCancelingOrCanceled() {
*/
String getExternalRef();

/**
* @return the username that triggered this action (directly or indirectly)
*/
String getTriggeredBy();

/**
* checks if the {@link #getForcedTime()} is hit by the given
* {@code hitTimeMillis}, by means if the given milliseconds are greater
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,8 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
*/
Optional<Integer> getAutoAssignWeight();

/**
* @return the user that triggered the auto assignment
*/
String getAutoAssignTriggeredBy();
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,33 +182,41 @@ public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
.map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build())
.collect(Collectors.toList());

return assignDistributionSets(deploymentRequests, null, offlineDsAssignmentStrategy);
return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null,
offlineDsAssignmentStrategy);
}

@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> assignDistributionSets(
public List<DistributionSetAssignmentResult> assignDistributionSets(final String triggeredBy,
a-sayyed marked this conversation as resolved.
Show resolved Hide resolved
final List<DeploymentRequest> deploymentRequests) {
return assignDistributionSets(deploymentRequests, null);
return assignDistributionSets(triggeredBy, deploymentRequests, null);
}

@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> assignDistributionSets(
final List<DeploymentRequest> deploymentRequests) {
return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests);
}

@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> assignDistributionSets(final String triggeredBy,
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
.validate(deploymentRequests);
return assignDistributionSets(deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
return assignDistributionSets(triggeredBy, deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
}

private List<DistributionSetAssignmentResult> assignDistributionSets(
private List<DistributionSetAssignmentResult> assignDistributionSets(final String triggeredBy,
final List<DeploymentRequest> deploymentRequests, final String actionMessage,
final AbstractDsAssignmentStrategy strategy) {
final List<DeploymentRequest> validatedRequests = validateRequestForAssignments(deploymentRequests);
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);

final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
.map(entry -> assignDistributionSetToTargetsWithRetry(entry.getKey(), entry.getValue(), actionMessage,
.map(entry -> assignDistributionSetToTargetsWithRetry(triggeredBy, entry.getKey(), entry.getValue(), actionMessage,
strategy))
.collect(Collectors.toList());
strategy.sendDeploymentEvents(results);
Expand Down Expand Up @@ -238,11 +246,11 @@ private static void checkIfRequiresMultiAssignment(final Collection<DeploymentRe
}
}

private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final Long dsID,
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String triggeredBy, final Long dsID,
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback = retryContext -> assignDistributionSetToTargets(
dsID, targetsWithActionType, actionMessage, assignmentStrategy);
triggeredBy, dsID, targetsWithActionType, actionMessage, assignmentStrategy);
return retryTemplate.execute(retryCallback);
}

Expand All @@ -260,6 +268,8 @@ private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(
* status to {@link TargetUpdateStatus#IN_SYNC} <br/>
* D. does not send a {@link TargetAssignDistributionSetEvent}.<br/>
*
* @param triggeredBy
* the username of the user who triggered the assignment
* @param dsID
* the ID of the distribution set to assign
* @param targetsWithActionType
Expand All @@ -274,7 +284,7 @@ private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(final Long dsID,
private DistributionSetAssignmentResult assignDistributionSetToTargets(final String triggeredBy, final Long dsID,
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {

Expand All @@ -296,8 +306,9 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon
final List<TargetWithActionType> existingTargetsWithActionType = targetsWithActionType.stream()
.filter(target -> existingTargetIds.contains(target.getControllerId())).collect(Collectors.toList());

final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(existingTargetsWithActionType,
actionMessage, assignmentStrategy, distributionSetEntity, targetEntities);
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(triggeredBy,
existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSetEntity,
targetEntities);
return buildAssignmentResult(distributionSetEntity, assignedActions, existingTargetsWithActionType.size());
}

Expand All @@ -310,7 +321,7 @@ private DistributionSetAssignmentResult allTargetsAlreadyAssignedResult(
Collections.emptyList());
}

private List<JpaAction> doAssignDistributionSetToTargets(
private List<JpaAction> doAssignDistributionSetToTargets(final String triggeredBy,
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy, final JpaDistributionSet distributionSetEntity,
final List<JpaTarget> targetEntities) {
Expand All @@ -325,8 +336,8 @@ private List<JpaAction> doAssignDistributionSetToTargets(
targetEntitiesIdsChunks.forEach(this::cancelInactiveScheduledActionsForTargets);
setAssignedDistributionSetAndTargetUpdateStatus(assignmentStrategy, distributionSetEntity,
targetEntitiesIdsChunks);
final List<JpaAction> assignedActions = createActions(targetsWithActionType, targetEntities, assignmentStrategy,
distributionSetEntity);
final List<JpaAction> assignedActions = createActions(triggeredBy, targetsWithActionType, targetEntities,
assignmentStrategy, distributionSetEntity);
// create initial action status when action is created so we remember
// the initial running status because we will change the status
// of the action itself and with this action status we have a nicer
Expand Down Expand Up @@ -417,12 +428,12 @@ private void setAssignedDistributionSetAndTargetUpdateStatus(final AbstractDsAss
assignmentStrategy.setAssignedDistributionSetAndTargetStatus(set, targetIdsChunks, currentUser);
}

private List<JpaAction> createActions(final Collection<TargetWithActionType> targetsWithActionType,
final List<JpaTarget> targets, final AbstractDsAssignmentStrategy assignmentStrategy,
final JpaDistributionSet set) {
private List<JpaAction> createActions(final String triggeredBy, final Collection<TargetWithActionType> targetsWithActionType,
final List<JpaTarget> targets, final AbstractDsAssignmentStrategy assignmentStrategy, final JpaDistributionSet set) {

return targetsWithActionType.stream().map(twt -> assignmentStrategy.createTargetAction(twt, targets, set))
.filter(Objects::nonNull).map(actionRepository::save).collect(Collectors.toList());
.filter(Objects::nonNull).peek(a -> a.setTriggeredBy(triggeredBy))
a-sayyed marked this conversation as resolved.
Show resolved Hide resolved
.map(actionRepository::save).collect(Collectors.toList());
}

private void createActionsStatus(final Collection<JpaAction> actions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ private void createScheduledAction(final Collection<Target> targets, final Distr
action.setStatus(Status.SCHEDULED);
action.setRollout(rollout);
action.setRolloutGroup(rolloutGroup);
action.setTriggeredBy(rollout.getCreatedBy());
a-sayyed marked this conversation as resolved.
Show resolved Hide resolved
rollout.getWeight().ifPresent(action::setWeight);
actionRepository.save(action);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
Expand Down Expand Up @@ -240,6 +240,7 @@ public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdat
targetFilterQuery.setAutoAssignDistributionSet(null);
targetFilterQuery.setAutoAssignActionType(null);
targetFilterQuery.setAutoAssignWeight(null);
targetFilterQuery.setAutoAssignTriggeredBy(null);
} else {
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update);
// we cannot be sure that the quota was enforced at creation time
Expand All @@ -248,8 +249,10 @@ public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdat
// auto-assign distribution set when creating a target filter query
assertMaxTargetsQuota(targetFilterQuery.getQuery());
final JpaDistributionSet ds = findDistributionSetAndThrowExceptionIfNotFound(update.getDsId());
final String triggeredBy = TenantAwareHolder.getInstance().getTenantAware().getCurrentUsername();
verifyDistributionSetAndThrowExceptionIfNotValid(ds);
targetFilterQuery.setAutoAssignDistributionSet(ds);
targetFilterQuery.setAutoAssignTriggeredBy(triggeredBy);
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType()));
targetFilterQuery.setAutoAssignWeight(update.getWeight());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**
* Checks if targets need a new distribution set (DS) based on the target filter
Expand Down Expand Up @@ -146,7 +147,10 @@ private int runTransactionalAssignment(final TargetFilterQuery targetFilterQuery
targetFilterQuery.getAutoAssignWeight().orElse(null), PAGE_SIZE);
final int count = deploymentRequests.size();
if (count > 0) {
deploymentManagement.assignDistributionSets(deploymentRequests, actionMessage);
final String triggeredBy = StringUtils.isEmpty(targetFilterQuery.getAutoAssignTriggeredBy()) ?
targetFilterQuery.getCreatedBy() :
targetFilterQuery.getAutoAssignTriggeredBy();
deploymentManagement.assignDistributionSets(triggeredBy, deploymentRequests, actionMessage);
}
return count;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "external_ref", length = Action.EXTERNAL_REF_MAX_LENGTH)
private String externalRef;

@Column(name = "triggered_by", updatable = false, nullable = false, length = 64)
a-sayyed marked this conversation as resolved.
Show resolved Hide resolved
private String triggeredBy;

@Override
public DistributionSet getDistributionSet() {
return distributionSet;
Expand Down Expand Up @@ -363,4 +366,13 @@ public void setExternalRef(final String externalRef) {
public String getExternalRef() {
return externalRef;
}

public void setTriggeredBy(final String triggeredBy) {
this.triggeredBy = triggeredBy;
}

@Override
public String getTriggeredBy() {
return triggeredBy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
@Column(name = "auto_assign_weight", nullable = true)
private Integer autoAssignWeight;

@Column(name = "auto_assign_triggered_by", nullable = true, length = 64)
private String autoAssignTriggeredBy;
schabdo marked this conversation as resolved.
Show resolved Hide resolved

public JpaTargetFilterQuery() {
// Default constructor for JPA.
}
Expand Down Expand Up @@ -149,6 +152,15 @@ public void setAutoAssignWeight(final Integer weight) {
this.autoAssignWeight = weight;
}

@Override
public String getAutoAssignTriggeredBy() {
return autoAssignTriggeredBy;
}

public void setAutoAssignTriggeredBy(final String autoAssignTriggeredBy) {
this.autoAssignTriggeredBy = autoAssignTriggeredBy;
}

@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE sp_action ADD COLUMN triggered_by VARCHAR(64) NOT NULL;
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_triggered_by VARCHAR(64);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE sp_action ADD COLUMN triggered_by VARCHAR(64) NOT NULL;
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_triggered_by VARCHAR(64);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE sp_action ADD COLUMN triggered_by VARCHAR(64) NOT NULL;
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_triggered_by VARCHAR(64);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE sp_action
ADD COLUMN triggered_by VARCHAR (64) NOT NULL;

ALTER TABLE sp_target_filter_query
ADD COLUMN auto_assign_triggered_by VARCHAR (64);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE sp_action ADD triggered_by VARCHAR(64) NOT NULL;
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_triggered_by VARCHAR(64);
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ public String getCurrentTenant() {
return null;
}

@Override
public String getCurrentUsername() {
final SecurityContext context = SecurityContextHolder.getContext();
if (context.getAuthentication() != null) {
final Object principal = context.getAuthentication().getPrincipal();
if (principal instanceof UserPrincipal) {
return ((UserPrincipal) principal).getUsername();
}
}
return null;
}

@Override
public <T> T runAsTenant(final String tenant, final TenantRunner<T> callable) {
final SecurityContext originalContext = SecurityContextHolder.getContext();
Expand Down