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

External reference for actions and status-update events #830

Merged
merged 13 commits into from
Jul 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,26 @@ Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<St
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Action cancelAction(long actionId);

/**
* Updates given {@link Action} with its external id.
*
* @param actionId
* to be updated
* @param externalRef
* of the action
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
smy4kor marked this conversation as resolved.
Show resolved Hide resolved
void updateActionExternalRef(long actionId, @NotEmpty String externalRef);

/**
* Retrieves list of {@link Action}s which matches the provided
* externalRefs.
*
* @param externalRefs
* for which the actions need to be fetched.
* @return list of {@link Action}s matching the externalRefs.
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
List<Action> getActiveActionsByExternalRef(@NotNull List<String> externalRefs);
smy4kor marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import javax.validation.constraints.NotEmpty;

/**
* Update operations to be executed by the target.
*/
Expand All @@ -32,6 +34,11 @@ public interface Action extends TenantAwareBaseEntity {
*/
int MAINTENANCE_WINDOW_TIMEZONE_LENGTH = 8;

/**
* Maximum length of external reference.
*/
int EXTERNAL_REF_MAX_LENGTH = 512;

/**
* @return the distributionSet
*/
Expand Down Expand Up @@ -98,6 +105,16 @@ default boolean isCancelingOrCanceled() {
*/
String getMaintenanceWindowTimeZone();

/**
* @param externalRef associated with this action
*/
void setExternalRef(@NotEmpty String externalRef);

/**
* @return externalRef of the action
*/
String getExternalRef();

/**
* 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 @@ -226,6 +226,18 @@ List<Long> findByTargetIdInAndIsActiveAndActionStatusAndDistributionSetNotRequir
@Param("targetsIds") List<Long> targetIds, @Param("active") boolean active,
@Param("currentStatus") Action.Status currentStatus);

/**
* Retrieves all {@link Action}s that matches the queried externalRefs.
*
* @param externalRefs
* for which the actions need to be found
* @param active
* flag to indicate active/inactive actions
* @return list of actions
*/
List<Action> findByExternalRefInAndActive(@Param("externalRefs") List<String> externalRefs,
@Param("active") boolean active);

/**
* Switches the status of actions from one specific status into another for
* given actions IDs, active flag and current status
Expand Down Expand Up @@ -490,4 +502,16 @@ Page<Action> findByRolloutIdAndRolloutGroupParentIsNullAndStatus(Pageable pageab
@Query("DELETE FROM JpaAction a WHERE a.id IN ?1")
void deleteByIdIn(Collection<Long> actionIDs);

/**
* Updates the externalRef of an action by its actionId.
*
* @param actionId
* for which the externalRef is being updated.
* @param externalRef
* value of the external reference for the given action id.
*/
@Modifying
@Transactional
@Query("UPDATE JpaAction a SET a.externalRef = :externalRef WHERE a.id = :actionId")
void updateExternalRef(@Param("actionId") Long actionId, @Param("externalRef") String externalRef);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
Expand Down Expand Up @@ -372,6 +374,11 @@ public Optional<Action> findActionWithDetails(final long actionId) {
return actionRepository.getById(actionId);
}

@Override
public List<Action> getActiveActionsByExternalRef(@NotNull final List<String> externalRefs) {
return actionRepository.findByExternalRefInAndActive(externalRefs, true);
smy4kor marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
Expand Down Expand Up @@ -1042,6 +1049,11 @@ public Action cancelAction(final long actionId) {
}
}

@Override
public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) {
actionRepository.updateExternalRef(actionId, externalRef);
}

private void cancelAssignDistributionSetEvent(final JpaTarget target, final Long actionId) {
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new CancelTargetAssignmentEvent(target, actionId, bus.getId())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio

@Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
private String maintenanceWindowTimeZone;

@Column(name = "external_ref", length = Action.EXTERNAL_REF_MAX_LENGTH)
private String externalRef;

@Override
public DistributionSet getDistributionSet() {
Expand Down Expand Up @@ -332,4 +335,14 @@ public boolean isMaintenanceWindowAvailable() {
}
}
}

@Override
public void setExternalRef(String externalRef) {
this.externalRef = externalRef;
}

@Override
public String getExternalRef() {
return externalRef;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE sp_action ADD COLUMN external_ref VARCHAR(512);
CREATE INDEX sp_idx_action_external_ref ON sp_action (external_ref);


Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE sp_action ADD COLUMN external_ref VARCHAR(512);
CREATE INDEX sp_idx_action_external_ref ON sp_action (external_ref);


Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE sp_action ADD COLUMN external_ref VARCHAR(512);
CREATE INDEX sp_idx_action_external_ref ON sp_action (external_ref);


Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE sp_action ADD external_ref VARCHAR(512);
CREATE INDEX sp_idx_action_external_ref ON sp_action (external_ref);


Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.io.ByteArrayInputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -54,6 +55,7 @@
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
Expand Down Expand Up @@ -1315,6 +1317,44 @@ public void quotaEceededExceptionWhenControllerReportsTooManyUpdateActionStatusM
}
}

@Test
@Description("Verify that the attaching externalRef to an action is propery stored")
public void updatedExternalRefOnActionIsReallyUpdated() {
final List<String> allExternalRef = new ArrayList<>();
final List<Long> allActionId = new ArrayList<>();
final int numberOfActions = 3;
final DistributionSet knownDistributionSet = testdataFactory.createDistributionSet();
for (int i = 0; i < numberOfActions; i++) {
final String knownControllerId = "controllerId" + i;
final String knownExternalref = "externalRefId" + i;

testdataFactory.createTarget(knownControllerId);
final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet(
knownDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId));
final Long actionId = assignmentResult.getActionIds().get(0);
controllerManagement.updateActionExternalRef(actionId, knownExternalref);

allExternalRef.add(knownExternalref);
allActionId.add(actionId);
}

final List<Action> foundAction = controllerManagement.getActiveActionsByExternalRef(allExternalRef);
assertThat(foundAction).isNotNull();
for (int i = 0; i < numberOfActions; i++) {
assertThat(foundAction.get(i).getId()).isEqualTo(allActionId.get(i));
}
}

@Test
@Description("Verify that a null externalRef cannot be assigned to an action")
public void externalRefCannotBeNull() {
try {
controllerManagement.updateActionExternalRef(1L, null);
fail("No ConstraintViolationException thrown when a null externalRef was set on an action");
} catch (final ConstraintViolationException e) {
}
}

@Test
@Description("Verifies that a target can report FINISHED/ERROR updates for DOWNLOAD_ONLY assignments regardless of "
+ "repositoryProperties.rejectActionStatusForClosedAction value.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
Expand Down Expand Up @@ -111,6 +117,42 @@ public <T> T runAsSystemAsTenant(final Callable<T> callable, final String tenant
}
}

/**
* Runs a given {@link Callable} within a system security context, which has
* the provided {@link GrantedAuthority}s to successfully run the
* {@link Callable}.
*
* The security context will be switched to the a new
* {@link SecurityContext} and back after the callable is called.
*
* @param tenant
* under which the {@link Callable#call()} must be executed.
* @param callable
* to call within the security context
* @return the return value of the {@link Callable#call()} method.
*/
// The callable API throws a Exception and not a specific one
@SuppressWarnings({ "squid:S2221", "squid:S00112" })
public <T> T runAsControllerAsTenant(@NotEmpty final String tenant, @NotNull final Callable<T> callable) {
smy4kor marked this conversation as resolved.
Show resolved Hide resolved
final SecurityContext oldContext = SecurityContextHolder.getContext();
List<SimpleGrantedAuthority> authorities = Collections
.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS));
try {
return tenantAware.runAsTenant(tenant, () -> {
try {
setCustomSecurityContext(tenant, oldContext.getAuthentication().getPrincipal(), authorities);
return callable.call();

} catch (final Exception e) {
throw new RuntimeException(e);
}
});

} finally {
SecurityContextHolder.setContext(oldContext);
}
}

smy4kor marked this conversation as resolved.
Show resolved Hide resolved
/**
* @return {@code true} if the current running code is running as system
* code block.
Expand All @@ -119,6 +161,16 @@ public boolean isCurrentThreadSystemCode() {
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
}

private void setCustomSecurityContext(final String tenantId, final Object principal,
final Collection<? extends GrantedAuthority> authorities) {
final AnonymousAuthenticationToken authenticationToken = new AnonymousAuthenticationToken(
UUID.randomUUID().toString(), principal, authorities);
authenticationToken.setDetails(new TenantAwareAuthenticationDetails(tenantId, true));
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
securityContextImpl.setAuthentication(authenticationToken);
SecurityContextHolder.setContext(securityContextImpl);
}

private static void setSystemContext(final SecurityContext oldContext) {
final Authentication oldAuthentication = oldContext.getAuthentication();
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
Expand Down