Skip to content

Commit

Permalink
rp.launch.uuid.creation.skip configuration properties to control La…
Browse files Browse the repository at this point in the history
…unch start on provided UUID
  • Loading branch information
HardNorth committed Nov 14, 2024
1 parent 1ef8721 commit f910d59
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 93 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## [Unreleased]
### Added
- `rp.launch.uuid.creation.skip` configuration properties to control Launch start on provided UUID, by @HardNorth

## [5.2.20]
### Fixed
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ etc.
| rp.truncation.item.name.limit | Integer | Default: `1024`<br> Maximum item names length before truncation. |
| rp.truncation.attribute.limit | Integer | Default: `128`<br> Maximum attribute key and value limit (counts separately) |

### Bug Tracking System parameters

| **Property name** | **Type** | **Description** |
|-------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| rp.bts.project | String | Bug Tracking System Project name to use along with `@ExternalIssue` annotation. Should be the same as in corresponding integration. |
| rp.bts.url | String | Bug Tracking System base URL. Should be the same as in corresponding integration. |
| rp.bts.issue.url | String | Bug Tracking System URL Pattern for Issues. Use <code>{issue_id}</code> and <code>{bts_project}</code> placeholders to mark a place where to put Issue ID and Bug Tracking System Project name. |
| rp.bts.issue.fail | Boolean | Default: `true`<br> Fail tests marked with `@Issue` annotation if they passed. Designed to not miss the moment when the issue got fixed but test is still marked by annotation. |

## Proxy configuration

ReportPortal supports 2 options for setting Proxy configuration:
Expand Down
58 changes: 34 additions & 24 deletions README_TEMPLATE.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class ListenerParameters implements Cloneable {
// Due to shortcoming of payload calculation mechanism this value is set to 65 million of bytes rather than 65 megabytes
public static final long DEFAULT_BATCH_PAYLOAD_LIMIT = 65 * 1000 * 1000;

public static final boolean DEFAULT_LAUNCH_CREATION_SKIP = true;
public static final boolean DEFAULT_LAUNCH_UUID_PRINT = false;
public static final String DEFAULT_LAUNCH_UUID_OUTPUT = "stdout";

Expand Down Expand Up @@ -122,6 +123,7 @@ public class ListenerParameters implements Cloneable {
private String truncateReplacement;
private int attributeLengthLimit;

private boolean isLaunchUuidCreationSkip;
private boolean printLaunchUuid;
private PrintStream printLaunchUuidOutput;

Expand Down Expand Up @@ -155,10 +157,12 @@ private static ChronoUnit toChronoUnit(@Nonnull TimeUnit t) {
@Nullable
private static Duration getDurationProperty(@Nonnull PropertiesLoader properties, @Nonnull ListenerProperty value,
@Nonnull ListenerProperty unit) {
return ofNullable(properties.getProperty(value)).map(Long::parseLong).map(t -> Duration.of(t,
ofNullable(properties.getProperty(unit)).map(u -> toChronoUnit(TimeUnit.valueOf(u)))
.orElse(ChronoUnit.MILLIS)
)).orElse(null);
return ofNullable(properties.getProperty(value)).map(Long::parseLong)
.map(t -> Duration.of(
t,
ofNullable(properties.getProperty(unit)).map(u -> toChronoUnit(TimeUnit.valueOf(u))).orElse(ChronoUnit.MILLIS)
))
.orElse(null);
}

/**
Expand Down Expand Up @@ -200,8 +204,8 @@ public ListenerParameters() {
this.attributeLengthLimit = DEFAULT_TRUNCATE_ATTRIBUTE_LIMIT;

this.printLaunchUuid = DEFAULT_LAUNCH_UUID_PRINT;
this.printLaunchUuidOutput =
OutputTypes.valueOf(DEFAULT_LAUNCH_UUID_OUTPUT.toUpperCase(Locale.ROOT)).getOutput();
this.printLaunchUuidOutput = OutputTypes.valueOf(DEFAULT_LAUNCH_UUID_OUTPUT.toUpperCase(Locale.ROOT)).getOutput();
this.isLaunchUuidCreationSkip = DEFAULT_LAUNCH_CREATION_SKIP;

this.btsIssueFail = DEFAULT_BTS_ISSUE_FAIL;
}
Expand All @@ -213,28 +217,22 @@ public ListenerParameters() {
*/
public ListenerParameters(PropertiesLoader properties) {
this.description = properties.getProperty(DESCRIPTION);
this.apiKey = ofNullable(properties.getProperty(API_KEY, properties.getProperty(UUID))).map(String::trim)
.orElse(null);
this.apiKey = ofNullable(properties.getProperty(API_KEY, properties.getProperty(UUID))).map(String::trim).orElse(null);
this.baseUrl = properties.getProperty(BASE_URL) != null ? properties.getProperty(BASE_URL).trim() : null;
this.proxyUrl = properties.getProperty(HTTP_PROXY_URL);
this.proxyUser = properties.getProperty(HTTP_PROXY_USER);
this.proxyPassword = properties.getProperty(HTTP_PROXY_PASSWORD);
this.httpLogging = properties.getPropertyAsBoolean(HTTP_LOGGING, DEFAULT_HTTP_LOGGING);

this.httpCallTimeout = getDurationProperty(properties, HTTP_CALL_TIMEOUT_VALUE, HTTP_CALL_TIMEOUT_UNIT);
this.httpConnectTimeout = getDurationProperty(properties,
HTTP_CONNECT_TIMEOUT_VALUE,
HTTP_CONNECT_TIMEOUT_UNIT
);
this.httpConnectTimeout = getDurationProperty(properties, HTTP_CONNECT_TIMEOUT_VALUE, HTTP_CONNECT_TIMEOUT_UNIT);
this.httpReadTimeout = getDurationProperty(properties, HTTP_READ_TIMEOUT_VALUE, HTTP_READ_TIMEOUT_UNIT);
this.httpWriteTimeout = getDurationProperty(properties, HTTP_WRITE_TIMEOUT_VALUE, HTTP_WRITE_TIMEOUT_UNIT);

this.projectName =
properties.getProperty(PROJECT_NAME) != null ? properties.getProperty(PROJECT_NAME).trim() : null;
this.projectName = properties.getProperty(PROJECT_NAME) != null ? properties.getProperty(PROJECT_NAME).trim() : null;
this.launchName = properties.getProperty(LAUNCH_NAME);
this.launchUuid = properties.getProperty(LAUNCH_UUID);
this.attributes = Collections.unmodifiableSet(AttributeParser.parseAsSet(properties.getProperty(
LAUNCH_ATTRIBUTES)));
this.attributes = Collections.unmodifiableSet(AttributeParser.parseAsSet(properties.getProperty(LAUNCH_ATTRIBUTES)));
this.launchRunningMode = parseLaunchMode(properties.getProperty(MODE));
this.enable = properties.getPropertyAsBoolean(ENABLE, DEFAULT_ENABLE);
this.isSkippedAnIssue = properties.getPropertyAsBoolean(SKIPPED_AS_ISSUE, DEFAULT_SKIP_ISSUE);
Expand All @@ -250,52 +248,39 @@ public ListenerParameters(PropertiesLoader properties) {
this.rerunOf = properties.getProperty(RERUN_OF);

this.asyncReporting = properties.getPropertyAsBoolean(ASYNC_REPORTING, DEFAULT_ASYNC_REPORTING);
this.callbackReportingEnabled = properties.getPropertyAsBoolean(CALLBACK_REPORTING_ENABLED,
DEFAULT_CALLBACK_REPORTING_ENABLED
);
this.callbackReportingEnabled = properties.getPropertyAsBoolean(CALLBACK_REPORTING_ENABLED, DEFAULT_CALLBACK_REPORTING_ENABLED);

this.ioPoolSize = properties.getPropertyAsInt(IO_POOL_SIZE, DEFAULT_IO_POOL_SIZE);

// client join parameters
clientJoin = properties.getPropertyAsBoolean(CLIENT_JOIN_MODE, DEFAULT_CLIENT_JOIN);
clientJoinMode = LaunchIdLockMode.valueOf(properties.getProperty(CLIENT_JOIN_MODE_VALUE,
DEFAULT_CLIENT_JOIN_MODE
));
clientJoinMode = LaunchIdLockMode.valueOf(properties.getProperty(CLIENT_JOIN_MODE_VALUE, DEFAULT_CLIENT_JOIN_MODE));
lockPortNumber = properties.getPropertyAsInt(CLIENT_JOIN_LOCK_PORT, DEFAULT_CLIENT_JOIN_LOCK_PORT);
lockFileName = properties.getProperty(FILE_LOCK_NAME, DEFAULT_LOCK_FILE_NAME);
syncFileName = properties.getProperty(FILE_SYNC_NAME, DEFAULT_SYNC_FILE_NAME);
clientJoinTimeout = ofNullable(properties.getProperty(CLIENT_JOIN_TIMEOUT_VALUE))
.map(t -> TimeUnit.valueOf(properties.getProperty(CLIENT_JOIN_TIMEOUT_UNIT,
clientJoinTimeout = ofNullable(properties.getProperty(CLIENT_JOIN_TIMEOUT_VALUE)).map(t -> TimeUnit.valueOf(properties.getProperty(CLIENT_JOIN_TIMEOUT_UNIT,
DEFAULT_CLIENT_JOIN_TIMEOUT_UNIT
)).toMillis(Long.parseLong(t)))
.orElse(DEFAULT_CLIENT_JOIN_TIMEOUT);
lockWaitTimeout = ofNullable(properties.getProperty(CLIENT_JOIN_LOCK_TIMEOUT_VALUE))
.map(t -> TimeUnit.valueOf(properties.getProperty(CLIENT_JOIN_LOCK_TIMEOUT_UNIT,
lockWaitTimeout = ofNullable(properties.getProperty(CLIENT_JOIN_LOCK_TIMEOUT_VALUE)).map(t -> TimeUnit.valueOf(properties.getProperty(CLIENT_JOIN_LOCK_TIMEOUT_UNIT,
DEFAULT_CLIENT_JOIN_LOCK_TIMEOUT_UNIT
)).toMillis(Long.parseLong(t)))
.orElse(DEFAULT_FILE_WAIT_TIMEOUT);
clientJoinLaunchTimeout = ofNullable(properties.getProperty(CLIENT_JOIN_LAUNCH_TIMEOUT_VALUE))
.map(t -> TimeUnit.valueOf(properties.getProperty(CLIENT_JOIN_LAUNCH_TIMEOUT_UNIT,
clientJoinLaunchTimeout = ofNullable(properties.getProperty(CLIENT_JOIN_LAUNCH_TIMEOUT_VALUE)).map(t -> TimeUnit.valueOf(properties.getProperty(CLIENT_JOIN_LAUNCH_TIMEOUT_UNIT,
DEFAULT_CLIENT_JOIN_LAUNCH_TIMEOUT_UNIT
)).toMillis(Long.parseLong(t)))
.orElse(DEFAULT_CLIENT_JOIN_LAUNCH_TIMEOUT);

this.rxBufferSize = properties.getPropertyAsInt(RX_BUFFER_SIZE, DEFAULT_RX_BUFFER_SIZE);

this.truncateFields = properties.getPropertyAsBoolean(TRUNCATE_FIELDS, DEFAULT_TRUNCATE);
this.truncateItemNamesLimit = properties.getPropertyAsInt(TRUNCATE_ITEM_NAME_LIMIT,
DEFAULT_TRUNCATE_ITEM_NAMES_LIMIT);
this.truncateItemNamesLimit = properties.getPropertyAsInt(TRUNCATE_ITEM_NAME_LIMIT, DEFAULT_TRUNCATE_ITEM_NAMES_LIMIT);
this.truncateReplacement = properties.getProperty(TRUNCATE_REPLACEMENT, DEFAULT_TRUNCATE_REPLACEMENT);
this.attributeLengthLimit = properties.getPropertyAsInt(TRUNCATE_ATTRIBUTE_LIMIT,
DEFAULT_TRUNCATE_ATTRIBUTE_LIMIT);
this.attributeLengthLimit = properties.getPropertyAsInt(TRUNCATE_ATTRIBUTE_LIMIT, DEFAULT_TRUNCATE_ATTRIBUTE_LIMIT);

this.printLaunchUuid = properties.getPropertyAsBoolean(LAUNCH_UUID_PRINT, DEFAULT_LAUNCH_UUID_PRINT);
this.printLaunchUuidOutput =
OutputTypes.valueOf(
properties
.getProperty(LAUNCH_UUID_PRINT_OUTPUT, DEFAULT_LAUNCH_UUID_OUTPUT)
.toUpperCase(Locale.ROOT)
).getOutput();
this.printLaunchUuidOutput = OutputTypes.valueOf(properties.getProperty(LAUNCH_UUID_PRINT_OUTPUT, DEFAULT_LAUNCH_UUID_OUTPUT)
.toUpperCase(Locale.ROOT)).getOutput();

this.btsProjectId = properties.getProperty(BTS_PROJECT);
this.btsUrl = properties.getProperty(BTS_URL);
Expand Down Expand Up @@ -380,6 +365,14 @@ public void setLaunchUuid(@Nullable String launchUuid) {
this.launchUuid = launchUuid;
}

public boolean isLaunchUuidCreationSkip() {
return isLaunchUuidCreationSkip;
}

public void setLaunchUuidCreationSkip(boolean launchUuidCreationSkip) {
isLaunchUuidCreationSkip = launchUuidCreationSkip;
}

public boolean isPrintLaunchUuid() {
return printLaunchUuid;
}
Expand Down Expand Up @@ -590,9 +583,7 @@ public void setHttpLogging(boolean httpLogging) {
}

public int getRxBufferSize() {
return ofNullable(System.getProperty("rx2.buffer-size")).map(Integer::valueOf)
.map(s -> Math.max(1, s))
.orElse(rxBufferSize);
return ofNullable(System.getProperty("rx2.buffer-size")).map(Integer::valueOf).map(s -> Math.max(1, s)).orElse(rxBufferSize);
}

public void setRxBufferSize(int size) {
Expand Down Expand Up @@ -743,6 +734,7 @@ public String toString() {
sb.append(", projectName='").append(projectName).append('\'');
sb.append(", launchName='").append(launchName).append('\'');
sb.append(", launchUuid='").append(launchUuid).append('\'');
sb.append(", launchUuidCreationSkip='").append(isLaunchUuidCreationSkip).append('\'');
sb.append(", printLaunchUuid='").append(printLaunchUuid).append('\'');
sb.append(", printLaunchUuidOutput='").append(printLaunchUuidOutput).append('\'');
sb.append(", launchRunningMode=").append(launchRunningMode);
Expand Down
24 changes: 15 additions & 9 deletions src/main/java/com/epam/reportportal/service/LaunchImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@ public class LaunchImpl extends Launch {
&& ErrorType.FINISH_ITEM_NOT_ALLOWED.equals(((ReportPortalException) throwable).getError().getErrorType()))
|| INTERNAL_CLIENT_EXCEPTION_PREDICATE.test(throwable);

private static final RetryWithDelay DEFAULT_REQUEST_RETRY = new RetryWithDelay(INTERNAL_CLIENT_EXCEPTION_PREDICATE,
private static final RetryWithDelay DEFAULT_REQUEST_RETRY = new RetryWithDelay(
INTERNAL_CLIENT_EXCEPTION_PREDICATE,
DEFAULT_RETRY_COUNT,
TimeUnit.SECONDS.toMillis(DEFAULT_RETRY_TIMEOUT)
);
private static final RetryWithDelay TEST_ITEM_FINISH_REQUEST_RETRY = new RetryWithDelay(TEST_ITEM_FINISH_RETRY_PREDICATE,
private static final RetryWithDelay TEST_ITEM_FINISH_REQUEST_RETRY = new RetryWithDelay(
TEST_ITEM_FINISH_RETRY_PREDICATE,
ITEM_FINISH_MAX_RETRIES,
TimeUnit.SECONDS.toMillis(ITEM_FINISH_RETRY_TIMEOUT)
);
Expand Down Expand Up @@ -127,10 +129,12 @@ protected LaunchImpl(@Nonnull final ReportPortalClient reportPortalClient, @Nonn
}).subscribeOn(getScheduler()).cache();

//noinspection ResultOfMethodCallIgnored
launchPromise.subscribe(rs -> emitter.onSuccess(rs.getId()), t -> {
LOG_ERROR.accept(t);
emitter.onComplete();
});
launchPromise.subscribe(
rs -> emitter.onSuccess(rs.getId()), t -> {
LOG_ERROR.accept(t);
emitter.onComplete();
}
);
}).cache();
projectSettings = ofNullable(getClient().getProjectSettings()).map(settings -> settings.subscribeOn(getScheduler()).cache())
.orElse(Maybe.empty());
Expand Down Expand Up @@ -213,14 +217,16 @@ private Set<ItemAttributesRQ> truncateAttributes(@Nullable final Set<ItemAttribu
ItemAttributesRQ updated = attribute;
int keyLength = ofNullable(updated.getKey()).map(String::length).orElse(0);
if (keyLength > limit && keyLength > replacement.length()) {
updated = new ItemAttributesRQ(updated.getKey().substring(0, limit - replacement.length()) + replacement,
updated = new ItemAttributesRQ(
updated.getKey().substring(0, limit - replacement.length()) + replacement,
updated.getValue(),
updated.isSystem()
);
}
int valueLength = ofNullable(updated.getValue()).map(String::length).orElse(0);
if (valueLength > limit && valueLength > replacement.length()) {
updated = new ItemAttributesRQ(updated.getKey(),
updated = new ItemAttributesRQ(
updated.getKey(),
updated.getValue().substring(0, limit - replacement.length()) + replacement,
updated.isSystem()
);
Expand Down Expand Up @@ -275,7 +281,7 @@ public Maybe<String> start() {
public void finish(final FinishExecutionRQ request) {
QUEUE.getOrCompute(launch).addToQueue(LaunchLoggingContext.complete());
Completable finish = Completable.concat(QUEUE.getOrCompute(launch).getChildren());
if (StringUtils.isBlank(getParameters().getLaunchUuid())) {
if (StringUtils.isBlank(getParameters().getLaunchUuid()) || !getParameters().isLaunchUuidCreationSkip()) {
FinishExecutionRQ rq = clonePojo(request, FinishExecutionRQ.class);
truncateAttributes(rq);
finish = finish.andThen(launch.map(id -> getClient().finishLaunch(id, rq)
Expand Down
45 changes: 27 additions & 18 deletions src/main/java/com/epam/reportportal/service/ReportPortal.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

Expand Down Expand Up @@ -118,34 +121,42 @@ public Launch newLaunch(@Nonnull StartLaunchRQ rq) {
return Launch.NOOP_LAUNCH;
}

if (StringUtils.isNotBlank(parameters.getLaunchUuid())) {
// a Launch UUID specified, use it and do not start a new Launch
return new LaunchImpl(rpClient, parameters, Maybe.just(parameters.getLaunchUuid()), executor);
StartLaunchRQ rqCopy = clonePojo(rq, StartLaunchRQ.class);
String launchUuid = parameters.getLaunchUuid();
boolean launchUuidSet = StringUtils.isNotBlank(launchUuid);
if (launchUuidSet) {
if (parameters.isLaunchUuidCreationSkip()) {
// a Launch UUID specified, but we should skip its creation
return new LaunchImpl(rpClient, parameters, Maybe.just(launchUuid), executor);
} else {
// a Launch UUID specified, but we should create a new Launch with it
rqCopy.setUuid(launchUuid);
}
}

if (launchIdLock == null) {
// do not use multi-client mode
return new LaunchImpl(rpClient, parameters, rq, executor);
return new LaunchImpl(rpClient, parameters, rqCopy, executor);
}

final String instanceUuid = UUID.randomUUID().toString();
final String uuid = launchIdLock.obtainLaunchUuid(instanceUuid);
if (uuid == null) {
// timeout locking on file or interrupted, anyway it should be logged already
// we continue to operate normally, since this flag is set by default and we shouldn't fail launches because of it
return new LaunchImpl(rpClient, parameters, rq, executor);
// we continue to operate normally, since this flag is set by default, and we shouldn't fail launches because of it
return new LaunchImpl(rpClient, parameters, rqCopy, executor);
}

if (instanceUuid.equals(uuid)) {
// We got our own UUID as launch UUID, that means we are primary launch.
StartLaunchRQ rqCopy = clonePojo(rq, StartLaunchRQ.class);
rqCopy.setUuid(uuid);
// We got our own instance UUID, that means we are primary launch.
if (!launchUuidSet) {
// If we got Launch UUID from parameters, we should use it, otherwise we should use instance UUID as Launch UUID
rqCopy.setUuid(instanceUuid);
}
return new PrimaryLaunch(rpClient, parameters, rqCopy, executor, launchIdLock, instanceUuid);
} else {
Maybe<String> launch = Maybe.create(emitter -> {
emitter.onSuccess(uuid);
emitter.onComplete();
});
// If we got Launch UUID from parameters, we should use it, otherwise we should use obtained UUID as a Secondary Launch
Maybe<String> launch = launchUuidSet ? Maybe.just(launchUuid) : Maybe.just(uuid);
return new SecondaryLaunch(rpClient, parameters, launch, executor, launchIdLock, instanceUuid);
}
}
Expand Down Expand Up @@ -495,12 +506,10 @@ protected Retrofit buildRestEndpoint(@Nonnull final ListenerParameters parameter
builder.baseUrl(baseUrl);
} catch (NoSuchMethodError e) {
throw new InternalReportPortalClientException(
"Unable to initialize OkHttp client. "
+ "ReportPortal client supports OkHttp version 3.11.0 as minimum.\n"
"Unable to initialize OkHttp client. ReportPortal client supports OkHttp version 3.11.0 as minimum.\n"
+ "Please update OkHttp dependency.\n"
+ "Besides this usually happens due to old selenium-java version (it overrides our dependency), "
+ "please use selenium-java 3.141.0 as minimum.",
e
+ "please use selenium-java 3.141.0 as minimum.", e
);
}
return builder.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.from(executor)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ public enum ListenerProperty {
PROJECT_NAME("rp.project", true),
LAUNCH_NAME("rp.launch", true),
/**
* Do not create new launch and use predefined Launch UUID.
* Use predefined Launch UUID.
*/
LAUNCH_UUID("rp.launch.uuid", false),
/**
* Do not create new launch and report to predefined Launch UUID.
*/
LAUNCH_UUID_CREATION_SKIP("rp.launch.uuid.creation.skip", false),
/**
* Print Launch UUID after start in a format: `ReportPortal Launch UUID: {UUID}`.
*/
Expand Down

0 comments on commit f910d59

Please sign in to comment.