Skip to content

Commit

Permalink
Break shadow refresh->modify->refresh cycle
Browse files Browse the repository at this point in the history
When refreshing shadow with pending modify operation, another refresh
was invoked, leading to application of another modify operation, and
so on. This caused application of modifications in wrong order.

This commit breaks this cycle of calls, so the order is now correct.

Related to MID-6795.
  • Loading branch information
mederly committed Mar 5, 2021
1 parent 027d99e commit 5e4844e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
Expand Up @@ -949,14 +949,15 @@ public String modifyShadow(PrismObject<ShadowType> repoShadow,
options.setForceRetry(Boolean.TRUE);
}

return modifyShadowAttempt(ctx, modifications, scripts, options, opState, task, parentResult);
return modifyShadowAttempt(ctx, modifications, scripts, options, opState, false, task, parentResult);
}

private String modifyShadowAttempt(ProvisioningContext ctx,
Collection<? extends ItemDelta> modifications,
OperationProvisioningScriptsType scripts,
ProvisioningOperationOptions options,
ProvisioningOperationState<AsynchronousOperationReturnValue<Collection<PropertyDelta<PrismPropertyValue>>>> opState,
boolean inRefresh,
Task task, OperationResult parentResult)
throws CommunicationException, GenericFrameworkException, ObjectNotFoundException,
SchemaException, ConfigurationException, SecurityViolationException, PolicyViolationException, ExpressionEvaluationException, EncryptionException, ObjectAlreadyExistsException {
Expand Down Expand Up @@ -987,7 +988,7 @@ private String modifyShadowAttempt(ProvisioningContext ctx,
LOGGER.trace("MODIFY {}: resource modification, execution starting\n{}", repoShadow, DebugUtil.debugDumpLazily(modifications));

RefreshShadowOperation refreshShadowOperation = null;
if (shouldRefresh(repoShadow)) {
if (!inRefresh && shouldRefresh(repoShadow)) {
refreshShadowOperation = refreshShadow(repoShadow, options, task, parentResult);
}

Expand Down Expand Up @@ -1751,7 +1752,7 @@ private void retryOperation(ProvisioningContext ctx,
if (pendingDelta.isModify()) {
modifyShadowAttempt(ctx, pendingDelta.getModifications(), scripts, options,
(ProvisioningOperationState<AsynchronousOperationReturnValue<Collection<PropertyDelta<PrismPropertyValue>>>>) opState,
task, result);
true, task, result);
}

if (pendingDelta.isDelete()) {
Expand Down
Expand Up @@ -592,6 +592,10 @@ public void test116RefreshAccountMorganRetrySuccess() throws Exception {
assertSteadyResources();
}

/**
* Tries to change the name to "Henry Morgan".
* Attempt will not succeed because of the communication failure.
*/
@Test
public void test120ModifyMorganFullNameCommunicationFailure() throws Exception {
Task task = getTestTask();
Expand Down Expand Up @@ -2067,6 +2071,73 @@ public void test900GetAccountMurrayPending() throws Exception {
assertSteadyResources();
}


/**
* Tries to change the name to "Henry Morgan" and then to "Captain Henry Morgan".
* Attempts will not succeed because of the communication failure.
*
* Then we make resource available and refresh the shadow.
* We verify that the operations are executed in the correct order.
*
* MID-6795
*/
@Test
public void test910ModifyMorganFullNameTwiceCommunicationFailure() throws Exception {
Task task = getTestTask();
OperationResult result = task.getResult();
syncServiceMock.reset();

clock.resetOverride();

dummyResource.setBreakMode(BreakMode.NONE);

PrismObject<ShadowType> account = prismContext.parseObject(ACCOUNT_MORGAN_FILE);
// Reset morgan OID. We cannot use the same OID, as there is a dead shadow with the original OID.
account.setOid(null);
account.checkConsistence();
display("Adding shadow", account);

shadowMorganOid = provisioningService.addObject(account, null, null, task, result);

dummyResource.setBreakMode(BreakMode.NETWORK);

ObjectDelta<ShadowType> delta1 = prismContext.deltaFactory().object().createModificationReplaceProperty(ShadowType.class,
shadowMorganOid, dummyResourceCtl.getAttributeFullnamePath(), ACCOUNT_MORGAN_FULLNAME_HM);
displayDumpable("ObjectDelta", delta1);

ObjectDelta<ShadowType> delta2 = prismContext.deltaFactory().object().createModificationReplaceProperty(ShadowType.class,
shadowMorganOid, dummyResourceCtl.getAttributeFullnamePath(), ACCOUNT_MORGAN_FULLNAME_CHM);
displayDumpable("ObjectDelta", delta2);

when("no connection");
provisioningService.modifyObject(ShadowType.class, delta1.getOid(), delta1.getModifications(),
null, null, task, result);
provisioningService.modifyObject(ShadowType.class, delta2.getOid(), delta2.getModifications(),
null, null, task, result);

when("no connection");
assertInProgress(result);

ShadowAsserter.forShadow(getShadowRepo(shadowMorganOid), "repository")
.display()
.pendingOperations()
.assertOperations(2);

when("connection reestablished, shadow refresh");
dummyResource.setBreakMode(BreakMode.NONE);

clockForward("PT1H");

PrismObject<ShadowType> shadow = provisioningService.getObject(ShadowType.class, shadowMorganOid, null, task, result);
provisioningService.refreshShadow(shadow, null, task, result);

then("connection reestablished, shadow refresh");
assertShadowProvisioning(shadowMorganOid)
.display()
.attributes()
.assertValue(dummyResourceCtl.getAttributeFullnameQName(), ACCOUNT_MORGAN_FULLNAME_CHM);
}

private void assertUncreatedMorgan(int expectedAttemptNumber) throws Exception {

// @formatter:off
Expand Down

0 comments on commit 5e4844e

Please sign in to comment.