Skip to content

Commit

Permalink
Add ResourceTestOptions
Browse files Browse the repository at this point in the history
These options drive the whole "test resource" operation. Their use
further streamlines the implementation and the API as well.

Work in progress.
  • Loading branch information
mederly committed May 21, 2022
1 parent 649909c commit 4337417
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public class OperationResult
public static final String PARAM_NAME = "name";
public static final String PARAM_TYPE = "type";
public static final String PARAM_OPTIONS = "options";
public static final String PARAM_RESOURCE = "resource";
public static final String PARAM_TASK = "task";
public static final String PARAM_OBJECT = "object";
public static final String PARAM_QUERY = "query";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ Object executeScript(String resourceOid, ProvisioningScriptType script, Task tas
* part is nicely displayable to the user. The operation codes in the returned {@link OperationResult} are defined by
* {@link TestResourceOpNames} enumeration class.
*
* See {@link ResourceTestOptions} for an explanation of the options and their default values.
*
* @param resourceOid OID of resource to test
* @return results of executed tests
* @throws ObjectNotFoundException resource or other required object (e.g. parent resource) does not exist
Expand All @@ -449,9 +451,17 @@ Object executeScript(String resourceOid, ProvisioningScriptType script, Task tas
*/
@NotNull OperationResult testResource(
@NotNull String resourceOid,
@Nullable ResourceTestOptions options,
@NotNull Task task,
@NotNull OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException;

@NotNull default OperationResult testResource(
@NotNull String resourceOid,
@NotNull Task task,
@NotNull OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException {
return testResource(resourceOid, null, task, parentResult);
}

/**
* Test the resource connection and basic resource connector functionality.
*
Expand All @@ -463,8 +473,9 @@ Object executeScript(String resourceOid, ProvisioningScriptType script, Task tas
* Notes:
*
* 1. The resource object must be mutable.
* 2. Normally it is expected that it will not have OID. But if it has, the method will assume the resource exists
* in the repository, and will update it there; just like {@link #testResource(String, Task, OperationResult)} does.
* 2. Normally it is expected that it will not have OID. But it may have one. The resource is _not_ updated
* in the repository, though, unless {@link ResourceTestOptions#skipRepositoryUpdates(Boolean)} is explicitly set
* to {@link Boolean#FALSE}.
*
* @param resource resource to test
* @return results of executed tests
Expand All @@ -474,27 +485,41 @@ Object executeScript(String resourceOid, ProvisioningScriptType script, Task tas
*/
@NotNull OperationResult testResource(
@NotNull PrismObject<ResourceType> resource,
@Nullable ResourceTestOptions options,
@NotNull Task task,
OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException;

default @NotNull OperationResult testResource(
@NotNull PrismObject<ResourceType> resource,
@NotNull Task task,
OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
return testResource(resource, null, task, parentResult);
}

/**
* Test basic resource connection.
*
* This operation will *not* throw exception in case the resource connection fails. For more information about operation
* result handling please see {@link #testResource(String, Task, OperationResult)} method description.
*
* TODO describe the exact difference to {@link #testResource(PrismObject, Task, OperationResult)}
* Actually, this is a convenience method for calling {@link #testResource(PrismObject, Task, OperationResult)} with
* the {@link ResourceTestOptions#testMode(ResourceTestOptions.TestMode)} set to {@link ResourceTestOptions.TestMode#BASIC}
* (more detailed explanation is in the `BASIC` value documentation).
*
* @param resource resource to test
* @return results of executed tests
* @throws GenericConnectorException unknown connector framework error
* @see TestResourceOpNames
*/
@NotNull OperationResult testPartialConfiguration(
default @NotNull OperationResult testPartialConfiguration(
@NotNull PrismObject<ResourceType> resource,
@NotNull Task task,
@NotNull OperationResult parentResult) throws SchemaException, ConfigurationException, ObjectNotFoundException;
@NotNull OperationResult parentResult) throws SchemaException, ConfigurationException, ObjectNotFoundException {
return testResource(
resource,
ResourceTestOptions.basic(),
task,
parentResult);
}

/**
* TODO please document this method
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.evolveum.midpoint.provisioning.api;

import com.evolveum.midpoint.schema.AbstractOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.ShortDumpable;

import java.io.Serializable;

import static com.evolveum.midpoint.util.MiscUtil.stateCheck;

/**
* Options for {@link ProvisioningService#testResource(String, Task, OperationResult)} operation.
*/
public class ResourceTestOptions extends AbstractOptions implements Serializable, Cloneable, ShortDumpable {

/** Full or basic? */
private final TestMode testMode;

/** Whether to update cached (in-definition) capabilities and schema. */
private final ResourceCompletionMode resourceCompletionMode;

/**
* If `true`, object in repository is not updated (not even by state change messages).
*
* The default is `false` for resources with non-null OID.
*/
private final Boolean skipRepositoryUpdates;

/**
* If `true`, object in memory is not updated (not even by state change messages).
*
* The default is `false` for resources with null OID.
*/
private final Boolean skipInMemoryUpdates;

public static final ResourceTestOptions DEFAULT = new ResourceTestOptions();

public ResourceTestOptions() {
this(null, null, null, null);
}

public ResourceTestOptions(
TestMode testMode,
ResourceCompletionMode resourceCompletionMode,
Boolean skipRepositoryUpdates,
Boolean skipInMemoryUpdates) {
this.testMode = testMode;
this.resourceCompletionMode = resourceCompletionMode;
this.skipRepositoryUpdates = skipRepositoryUpdates;
this.skipInMemoryUpdates = skipInMemoryUpdates;
}

public TestMode getTestMode() {
return testMode;
}

public ResourceTestOptions testMode(TestMode testMode) {
return new ResourceTestOptions(testMode, resourceCompletionMode, skipRepositoryUpdates, skipInMemoryUpdates);
}

public static ResourceTestOptions basic() {
return new ResourceTestOptions().testMode(TestMode.BASIC);
}

public boolean isFullMode() {
return testMode == null || testMode == TestMode.FULL;
}

public ResourceCompletionMode getResourceCompletionMode() {
return resourceCompletionMode;
}

public ResourceTestOptions resourceCompletionMode(ResourceCompletionMode resourceCompletionMode) {
return new ResourceTestOptions(testMode, resourceCompletionMode, skipRepositoryUpdates, skipInMemoryUpdates);
}

public Boolean isSkipRepositoryUpdates() {
return skipRepositoryUpdates;
}

public ResourceTestOptions skipRepositoryUpdates(Boolean skipRepositoryUpdates) {
return new ResourceTestOptions(testMode, resourceCompletionMode, skipRepositoryUpdates, skipInMemoryUpdates);
}

public Boolean isSkipInMemoryUpdates() {
return skipInMemoryUpdates;
}

public ResourceTestOptions skipInMemoryUpdates(Boolean skipInMemoryUpdates) {
return new ResourceTestOptions(testMode, resourceCompletionMode, skipRepositoryUpdates, skipInMemoryUpdates);
}

@Override
public ResourceTestOptions clone() {
try {
return (ResourceTestOptions) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("RepoAddOptions(");
shortDump(sb);
sb.append(")");
return sb.toString();
}

@Override
public void shortDump(StringBuilder sb) {
appendVal(sb, "testMode", testMode);
appendVal(sb, "resourceCompletionMode", resourceCompletionMode);
appendFlag(sb, "skipRepositoryUpdates", skipRepositoryUpdates);
appendFlag(sb, "skipInMemoryUpdates", skipInMemoryUpdates);
removeLastComma(sb);
}

/** Checks we leave nothing set to default. */
public void checkAllValuesSet() {
stateCheck(testMode != null, "Test mode not set in %s", this);
stateCheck(resourceCompletionMode != null, "Completion mode not set in %s", this);
stateCheck(skipRepositoryUpdates != null, "Repository updates not set in %s", this);
stateCheck(skipInMemoryUpdates != null, "In memory updates not set in %s", this);
}

public enum TestMode {
/**
* Traditional (full) test. This is the default.
*/
FULL,

/**
* Only the basic connectivity on the main connector is tested. Corresponds to "testPartialConfiguration" method.
* Resource in repository is never updated in this mode. Resource is never completed. (Attempt to set these
* options to enable this behavior leads to a failure.) In-memory updates are disabled by default, but can be
* enabled.
*/
BASIC
}

/** Whether capabilities and schema should be written back to the resource (into repository or in-memory version). */
public enum ResourceCompletionMode {

/** Never do the completion. */
NEVER,

/**
* Complete the resource if it's not complete. This updates both capabilities and schema
* if either one of them is missing. This is the legacy (pre-4.6) behavior, and is currently
* the default when repository update is carried out. TODO determine for in-memory updates only.
*/
IF_NOT_COMPLETE,

/** Always updates capabilities and schema. */
ALWAYS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ public class ProvisioningServiceImpl implements ProvisioningService, SystemConfi
private static final String OP_DISCOVER_CONFIGURATION = ProvisioningService.class.getName() + ".discoverConfiguration";
// TODO reconsider names of these operations
private static final String OP_TEST_RESOURCE = ProvisioningService.class.getName() + ".testResource";
private static final String OP_TEST_PARTIAL_CONFIGURATION = ProvisioningService.class.getName() + ".testPartialConfiguration";

@Autowired ShadowsFacade shadowsFacade;
@Autowired ResourceManager resourceManager;
Expand Down Expand Up @@ -583,19 +582,21 @@ public Object executeScript(String resourceOid, ProvisioningScriptType script, T
@Override
public @NotNull OperationResult testResource(
@NotNull String resourceOid,
@Nullable ResourceTestOptions options,
@NotNull Task task,
@NotNull OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
Validate.notNull(resourceOid, "Resource OID to test is null.");

OperationResult result = parentResult.subresult(OP_TEST_RESOURCE)
.addParam("resourceOid", resourceOid)
.addArbitraryObjectAsParam(OperationResult.PARAM_OPTIONS, options)
.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class)
.build();
try {
PrismObject<ResourceType> resource =
operationsHelper.getRepoObject(ResourceType.class, resourceOid, null, result);
return testResourceInternal(resource, task, result);
return testResourceInternal(resource, options, task, result);
} catch (Throwable t) {
result.recordFatalError(t);
throw t;
Expand All @@ -607,15 +608,25 @@ public Object executeScript(String resourceOid, ProvisioningScriptType script, T
@Override
public @NotNull OperationResult testResource(
@NotNull PrismObject<ResourceType> resource,
@Nullable ResourceTestOptions options,
@NotNull Task task,
@NotNull OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
OperationResult result = parentResult.subresult(OP_TEST_RESOURCE)
.addParam("resource", resource)
.addParam(OperationResult.PARAM_RESOURCE, resource)
.addArbitraryObjectAsParam(OperationResult.PARAM_OPTIONS, options)
.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class)
.build();
try {
return testResourceInternal(resource, task, result);
// The default for skipping repository updates here is "true", even if the resource has an OID.
// We update the repository object only if explicitly requested by the client.
if (options == null) {
options = new ResourceTestOptions().skipRepositoryUpdates(true);
} else if (options.isSkipRepositoryUpdates() == null) {
options = options.skipRepositoryUpdates(true);
}

return testResourceInternal(resource, options, task, result);
} catch (Throwable t) {
result.recordFatalError(t);
throw t;
Expand All @@ -625,38 +636,14 @@ public Object executeScript(String resourceOid, ProvisioningScriptType script, T
}

private OperationResult testResourceInternal(
@NotNull PrismObject<ResourceType> resource, Task task, OperationResult result)
@NotNull PrismObject<ResourceType> resource, @Nullable ResourceTestOptions options, Task task, OperationResult result)
throws SchemaException, ConfigurationException, ObjectNotFoundException {
LOGGER.trace("Starting testing {}", resource);
OperationResult testResult = resourceManager.testResource(resource, task, result);
OperationResult testResult = resourceManager.testResource(resource, options, task, result);
LOGGER.debug("Finished testing {}, result: {}", resource, testResult.getStatus());
return testResult;
}

@Override
public @NotNull OperationResult testPartialConfiguration(
@NotNull PrismObject<ResourceType> resource,
@NotNull Task task,
@NotNull OperationResult parentResult)
throws SchemaException, ConfigurationException, ObjectNotFoundException {

OperationResult result = parentResult.subresult(OP_TEST_PARTIAL_CONFIGURATION)
.addParam("resource", resource)
.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class)
.build();
try {
LOGGER.trace("Starting testing partial configuration for {}", resource);
OperationResult testResult = resourceManager.testPartialConfiguration(resource, task, result);
LOGGER.debug("Finished testing partial configuration for {}, result: {} ", resource, testResult.getStatus());
return testResult;
} catch (Throwable t) {
result.recordFatalError(t);
throw t;
} finally {
result.close();
}
}

@Override
public @NotNull DiscoveredConfiguration discoverConfiguration(
@NotNull PrismObject<ResourceType> resource, @NotNull OperationResult parentResult) {
Expand Down

0 comments on commit 4337417

Please sign in to comment.