diff --git a/sechub-administration/src/main/java/com/mercedesbenz/sechub/domain/administration/scheduler/SchedulerStatusEntryKeys.java b/sechub-administration/src/main/java/com/mercedesbenz/sechub/domain/administration/scheduler/SchedulerStatusEntryKeys.java
index 1ed3b3a216..2e2a4de99e 100644
--- a/sechub-administration/src/main/java/com/mercedesbenz/sechub/domain/administration/scheduler/SchedulerStatusEntryKeys.java
+++ b/sechub-administration/src/main/java/com/mercedesbenz/sechub/domain/administration/scheduler/SchedulerStatusEntryKeys.java
@@ -18,6 +18,8 @@ public enum SchedulerStatusEntryKeys implements StatusEntryKey {
SCHEDULER_JOBS_CANCEL_REQUESTED("status.scheduler.jobs.cancel_requested"),
+ SCHEDULER_JOBS_RESUMING("status.scheduler.jobs.resuming"),
+
SCHEDULER_JOBS_SUSPENDED("status.scheduler.jobs.suspended"),
SCHEDULER_JOBS_ENDED("status.scheduler.jobs.ended"),
diff --git a/sechub-api-java/src/main/resources/reduced-openapi3.json b/sechub-api-java/src/main/resources/reduced-openapi3.json
index 192b2d741e..d44eb27cf5 100644
--- a/sechub-api-java/src/main/resources/reduced-openapi3.json
+++ b/sechub-api-java/src/main/resources/reduced-openapi3.json
@@ -305,119 +305,57 @@
"title": "FalsePositives",
"type": "object",
"properties": {
- "falsePositives": {
+ "projectData": {
"type": "array",
- "description": "Job data list containing false positive setup based on former jobs",
+ "description": "Porject data list containing false positive setup for the project",
"items": {
"type": "object",
"properties": {
- "metaData": {
+ "webScan": {
"type": "object",
"properties": {
- "severity": {
- "type": "string",
- "description": "Severity of origin report entry marked as false positive"
- },
- "code": {
- "type": "object",
- "properties": {
- "start": {
- "type": "object",
- "properties": {
- "sourceCode": {
- "type": "string",
- "description": "source code"
- },
- "relevantPart": {
- "type": "string",
- "description": "relevant part of source vulnerability"
- },
- "location": {
- "type": "string",
- "description": "location of code"
- }
- },
- "description": "entry point"
- },
- "end": {
- "type": "object",
- "properties": {
- "sourceCode": {
- "type": "string",
- "description": "source code"
- },
- "relevantPart": {
- "type": "string",
- "description": "relevant part of source vulnerability"
- },
- "location": {
- "type": "string",
- "description": "location of code"
- }
- },
- "description": "end point (sink)"
- }
- },
- "description": "Code part. Only available for scan type 'codeScan'"
- },
- "owasp": {
- "type": "string",
- "description": "OWASP At least this field must be set for web scans when no cwe identifier is defined."
- },
"cweId": {
"type": "number",
- "description": "CWE (common weakness enumeration). For code scans this is always set."
- },
- "cveId": {
- "type": "string",
- "description": "CVE (common vulnerability and exposures). For infra scans this is always set."
- },
- "name": {
- "type": "string",
- "description": "Name of origin finding marked as false positive"
- },
- "scanType": {
- "type": "string",
- "description": "Scan type - e.g. codeScan"
- }
- },
- "description": "Meta data for this false positive"
- },
- "jobData": {
- "type": "object",
- "properties": {
- "jobUUID": {
- "type": "string",
- "description": "SecHub job uuid where finding was"
+ "description": "Defines a CWE ID for false positives which occur during webscans. This is mandatory, but can be empty. If it is not specified it matches the findings with no CWE IDs."
},
- "findingId": {
- "type": "number",
- "description": "SecHub finding identifier - identifies problem inside the job which shall be markeda as a false positive. *ATTENTION*: at the moment only code scan false positive handling is supported. Infra and web scan findings will lead to a non accepted error!"
+ "methods": {
+ "type": "array",
+ "description": "Defines a list of (HTTP) methods for false positives which occur during webscans. This is optional and if nothing is specified, the entry applies to all methods.",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object"
+ },
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ },
+ {
+ "type": "number"
+ }
+ ]
+ }
},
- "comment": {
+ "urlPattern": {
"type": "string",
- "description": "A comment from author describing why this was marked as a false positive"
+ "description": "Defines a url pattern for false positives which occur during webscans. Can be used with wildcards like '*.host.com'. Each entry must contain more than just wildcards, '*.*.*' or '*' are not allowed."
}
},
- "description": "Job data parts, can be used as key to identify false positives"
+ "description": "Defines a section for false positives which occur during webscans."
},
- "created": {
+ "comment": {
"type": "string",
- "description": "Creation timestamp"
+ "description": "A comment describing why this is a false positive."
},
- "author": {
+ "id": {
"type": "string",
- "description": "User id of author who created false positive"
+ "description": "Identifier which is used to update or remove the respective false positive entry."
}
}
}
- }
- }
- },
- "FalsePositivesForJob": {
- "title": "FalsePositivesForJob",
- "type": "object",
- "properties": {
+ },
"apiVersion": {
"type": "string",
"description": "The api version, currently only 1.0 is supported"
@@ -434,7 +372,7 @@
},
"findingId": {
"type": "number",
- "description": "SecHub finding identifier - identifies problem inside the job which shall be markeda as a false positive. *ATTENTION*: at the moment only code scan false positive handling is supported. Infra and web scan findings will lead to a non accepted error!"
+ "description": "SecHub finding identifier - identifies problem inside the job which shall be markeda as a false positive."
},
"comment": {
"type": "string",
@@ -445,7 +383,7 @@
},
"type": {
"type": "string",
- "description": "The type of the json content. Currently only accepted value is 'falsePositiveJobDataList'."
+ "description": "The type of the json content. Currently only accepted value is 'falsePositiveDataList' but we also still accept the deprecated type 'falsePositiveJobDataList'."
}
}
},
@@ -1035,6 +973,19 @@
"webScan": {
"type": "object",
"properties": {
+ "maxScanDuration": {
+ "type": "object",
+ "properties": {
+ "duration": {
+ "type": "number",
+ "description": "Duration of the scan as integer"
+ },
+ "unit": {
+ "type": "string",
+ "description": "Unit of the duration. Possible values are: millisecond(s), second(s), minute(s), hour(s), day(s)"
+ }
+ }
+ },
"headers": {
"type": "array",
"description": "List of HTTP headers. Can be used for authentication or anything else.",
@@ -1070,19 +1021,6 @@
}
}
},
- "maxScanDuration": {
- "type": "object",
- "properties": {
- "duration": {
- "type": "number",
- "description": "Duration of the scan as integer"
- },
- "unit": {
- "type": "string",
- "description": "Unit of the duration. Possible values are: millisecond(s), second(s), minute(s), hour(s), day(s)"
- }
- }
- },
"clientCertificate": {
"type": "object",
"properties": {
@@ -1683,7 +1621,7 @@
"201": {
"description": "201",
"content": {
- "application/json": {
+ "text/plain;charset=UTF-8": {
"schema": {
"$ref": "#/components/schemas/ExecutorConfigurationId"
}
@@ -3196,6 +3134,48 @@
}
}
},
+ "/api/project/{projectId}/false-positive/project-data/{id}": {
+ "delete": {
+ "tags": [
+ "project"
+ ],
+ "summary": "User unmarks existing false positive project data definitons",
+ "description": "User unmarks existing false positive project data definitons",
+ "operationId": "userUnmarksFalsePositivesProjectData",
+ "parameters": [
+ {
+ "name": "projectId",
+ "in": "path",
+ "description": "The project id",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "in": "path",
+ "description": "Identifier which is used to remove the respective false positive entry.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "204"
+ }
+ },
+ "security": [
+ {
+ "basic": [
+
+ ]
+ }
+ ]
+ }
+ },
"/api/project/{projectId}/false-positive/{jobUUID}/{findingId}": {
"delete": {
"tags": [
@@ -3290,9 +3270,9 @@
"tags": [
"project"
],
- "summary": "User marks false positives for finished sechub job",
- "description": "User marks false positives for finished sechub job",
- "operationId": "userMarksFalsePositivesForJob",
+ "summary": "User marks false positives",
+ "description": "User marks false positives",
+ "operationId": "userMarksFalsePositives",
"parameters": [
{
"name": "projectId",
@@ -3308,7 +3288,7 @@
"content": {
"application/json;charset=UTF-8": {
"schema": {
- "$ref": "#/components/schemas/FalsePositivesForJob"
+ "$ref": "#/components/schemas/FalsePositives"
}
}
}
diff --git a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/job/ExecutionState.java b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/job/ExecutionState.java
index 9a4c11ce2f..d25e0dc161 100644
--- a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/job/ExecutionState.java
+++ b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/job/ExecutionState.java
@@ -27,7 +27,11 @@ public enum ExecutionState {
SUSPENDED("The job has been suspended and can be resumed by another SecHub instance"),
- ENDED("Has ended - with failure or success");
+ RESUMING("A former suspended job is resuming"),
+
+ ENDED("Has ended - with failure or success"),
+
+ ;
private String description;
diff --git a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/util/DocGeneratorUtil.java b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/util/DocGeneratorUtil.java
index ce3eb44ec7..6e4208fe27 100644
--- a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/util/DocGeneratorUtil.java
+++ b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/util/DocGeneratorUtil.java
@@ -10,6 +10,7 @@
import com.mercedesbenz.sechub.docgen.DocAnnotationData;
import com.mercedesbenz.sechub.pds.PDSMustBeDocumented;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
public class DocGeneratorUtil {
@@ -27,7 +28,7 @@ public static DocAnnotationData buildDataForMustBeDocumented(MustBeDocumented in
buildSpringScheduledParts(data, element);
/* when class name shall be used... */
- if (MustBeDocumented.SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED.equals(data.scope)) {
+ if (DocumentationScopeConstants.SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED.equals(data.scope)) {
data.scope = toCamelOne(fetchClass(element)).toLowerCase();
}
return data;
@@ -42,7 +43,7 @@ public static DocAnnotationData buildDataForPDSMustBeDocumented(PDSMustBeDocumen
buildSpringScheduledParts(data, element);
/* when class name shall be used... */
- if (MustBeDocumented.SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED.equals(data.scope)) {
+ if (DocumentationScopeConstants.SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED.equals(data.scope)) {
data.scope = toCamelOne(fetchClass(element)).toLowerCase();
}
return data;
diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java
index cead2c830d..1e26c38bf5 100644
--- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java
+++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java
@@ -36,9 +36,11 @@
import com.mercedesbenz.sechub.commons.mapping.MappingData;
import com.mercedesbenz.sechub.commons.model.JSONConverter;
import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
+import com.mercedesbenz.sechub.commons.model.TrafficLight;
import com.mercedesbenz.sechub.domain.scan.project.FalsePositiveProjectData;
import com.mercedesbenz.sechub.integrationtest.JSONTestSupport;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestContext;
+import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestExampleConstants;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestFileSupport;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestTemplateFile;
import com.mercedesbenz.sechub.integrationtest.internal.SecHubClientExecutor.ExecutionResult;
@@ -965,12 +967,52 @@ public ProjectFalsePositivesDefinition startFalsePositiveDefinition(TestProject
return new ProjectFalsePositivesDefinition(project);
}
- public UUID triggerAsyncPDSCodeScanWithDifferentDataSections(TestProject project) {
+ /**
+ * This triggers a PDS code scan in asynchronous way. It will use
+ * {@link IntegrationTestExampleConstants#PATH_TO_ZIPFILE_WITH_DIFFERENT_DATA_SECTIONS}.
+ * It will map wanted traffic light to corresponding reference ids:
+ *
+ *
+ * Traffic light |
+ * used reference-id |
+ *
+ *
+ * GREEN |
+ * low-id |
+ *
+ *
+ * YELLOW |
+ * medium-id |
+ *
+ *
+ * RED |
+ * critical-id |
+ *
+ *
+ * Please look at
+ * {@link IntegrationTestExampleConstants#PATH_TO_ZIPFILE_WITH_DIFFERENT_DATA_SECTIONS}
+ * for the finding details.
+ *
+ * @param project - test project
+ * @param wantedTrafficLight - which kind of report is wanted
+ * @return uuid of SecHub job
+ */
+ public UUID triggerAsyncPDSCodeScanWithWantedTrafficLightResult(TestProject project, TrafficLight wantedTrafficLight) {
/* @formatter:off */
+ String referenceId = switch (wantedTrafficLight) {
+ case GREEN -> "low-id";
+ case YELLOW -> "medium-id";
+ case RED -> "critical-id";
+ default -> {
+ throw new IllegalArgumentException("Traffic light type: "+wantedTrafficLight+" is not supported by this test helper method!");
+ }
+ };
+
+
UUID jobUUID = createCodeScanWithTemplate(
IntegrationTestTemplateFile.CODE_SCAN_3_SOURCES_DATA_ONE_REFERENCE,
project, NOT_MOCKED,
- TemplateData.builder().addReferenceId("files-a").build());
+ TemplateData.builder().addReferenceId(referenceId).build());
uploadSourcecode(project, jobUUID, PATH_TO_ZIPFILE_WITH_DIFFERENT_DATA_SECTIONS).
approveJob(project, jobUUID);
diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/IntegrationTestSetup.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/IntegrationTestSetup.java
index f1680c112d..0273e82711 100644
--- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/IntegrationTestSetup.java
+++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/IntegrationTestSetup.java
@@ -30,7 +30,8 @@
import ch.qos.logback.classic.Level;
public class IntegrationTestSetup implements TestRule {
- // please keep this private static field at top - reason: initialize setup support as soon as possible...
+ // please keep this private static field at top - reason: initialize setup
+ // support as soon as possible...
private static final LocalDeveloperFileSetupSupport support = LocalDeveloperFileSetupSupport.INSTANCE;
public static final String SECHUB_INTEGRATIONTEST_ONLY_NECESSARY_TESTS_FOR_DOCUMENTATION = "sechub.integrationtest.only.necessary4documentation";
public static final String SECHUB_INTEGRATIONTEST_NEVER_NECESSARY_TESTS_FOR_DOCUMENTATION = "sechub.integrationtest.never.necessary4documentation";
@@ -166,8 +167,7 @@ public void evaluate() throws Throwable {
Assume.assumeTrue(message, false);
}
} else {
- integrationTestEnabled = support.isAlwaysSecHubIntegrationTestRunning()
- || Boolean.getBoolean(SECHUB_INTEGRATIONTEST_RUNNING);
+ integrationTestEnabled = support.isAlwaysSecHubIntegrationTestRunning() || Boolean.getBoolean(SECHUB_INTEGRATIONTEST_RUNNING);
if (!integrationTestEnabled) {
String message = "Skipped test scenario '" + scenario.getName() + "'\nReason: not in integration test mode.\nDefine -D"
+ SECHUB_INTEGRATIONTEST_RUNNING + "=true to enable integration tests!";
diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java
index 865896e9ae..a5e40b9fb9 100644
--- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java
+++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java
@@ -357,7 +357,7 @@ public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
public static TestSecHubJobStatus getSecHubJobStatus(TestProject project, UUID jobUUID, TestUser asUser) {
String status = as(asUser).getJobStatus(project.getProjectId(), jobUUID);
- LOG.info(">>>>>>>>>JOB:STATUS:" + status);
+ LOG.info(" => Job status: {}", status);
TestSecHubJobStatus jobStatus = TestSecHubJobStatus.fromJSON(status);
return jobStatus;
}
@@ -443,12 +443,10 @@ public static void waitForJobRunning(TestProject project, int timeOutInSeconds,
LOG.info("wait for job running project:{}, job:{}, timeToWaitInMillis{}, timeOutInSeconds:{}", project.getProjectId(), jobUUID, timeToWaitInMillis,
timeOutInSeconds);
- executeUntilSuccessOrTimeout(new AbstractTestExecutable(SUPER_ADMIN, timeOutInSeconds, timeToWaitInMillis, HttpClientErrorException.class) {
+ executeUntilSuccessOrTimeout(new AbstractTestExecutable(SUPER_ADMIN, timeOutInSeconds, HttpClientErrorException.class) {
@Override
public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
- String status = as(getUser()).getJobStatus(project.getProjectId(), jobUUID);
- LOG.info(">>>>>>>>>JOB:STATUS:" + status);
- return status.contains("STARTED");
+ return containsStatus(getUser(), project, jobUUID, "STARTED");
}
});
}
@@ -465,13 +463,24 @@ public static void waitForJobStatusCancelRequestedOrCanceled(TestProject project
executeUntilSuccessOrTimeout(new AbstractTestExecutable(SUPER_ADMIN, 5, HttpClientErrorException.class) {
@Override
public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
- String status = as(getUser()).getJobStatus(project.getProjectId(), jobUUID);
- LOG.info(">>>>>>>>>JOB:STATUS:" + status);
- return status.contains("CANCEL_REQUESTED") || status.contains("CANCELED");
+ return containsStatus(getUser(), project, jobUUID, "CANCEL_REQUESTED", "CANCELED");
}
});
}
+ private static boolean containsStatus(TestUser user, TestProject project, UUID jobUUID, String... acceptedContainedStatus) {
+ String status = as(user).getJobStatus(project.getProjectId(), jobUUID);
+ LOG.info(">>>>>>>>>JOB:STATUS:" + status);
+
+ for (String accepted : acceptedContainedStatus) {
+ if (status.contains(accepted)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Waits for sechub job being finally canceled - after 5 seconds time out is
* reached
@@ -489,9 +498,7 @@ public static void waitForJobStatusCanceled(TestProject project, UUID jobUUID, b
executeUntilSuccessOrTimeout(new AbstractTestExecutable(SUPER_ADMIN, 5, runnable, HttpClientErrorException.class) {
@Override
public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
- String status = as(getUser()).getJobStatus(project.getProjectId(), jobUUID);
- LOG.info(">>>>>>>>>JOB:STATUS:" + status);
- return status.contains("CANCELED");
+ return containsStatus(getUser(), project, jobUUID, "CANCELED");
}
});
}
@@ -505,13 +512,10 @@ public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
*/
public static void waitForJobStatusSuspended(TestProject project, UUID jobUUID) {
LOG.info("wait for job status is 'suspended'. project:{}, job:{}", project.getProjectId(), jobUUID);
-
executeUntilSuccessOrTimeout(new AbstractTestExecutable(SUPER_ADMIN, 5, HttpClientErrorException.class) {
@Override
public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
- String status = as(getUser()).getJobStatus(project.getProjectId(), jobUUID);
- LOG.info(">>>>>>>>>JOB:STATUS:" + status);
- return status.contains("SUSPENDED");
+ return containsStatus(getUser(), project, jobUUID, "SUSPENDED");
}
});
}
@@ -528,9 +532,7 @@ public static void waitForJobStatusFailed(TestProject project, UUID jobUUID) {
executeUntilSuccessOrTimeout(new AbstractTestExecutable(SUPER_ADMIN, 5, HttpClientErrorException.class) {
@Override
public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
- String status = as(getUser()).getJobStatus(project.getProjectId(), jobUUID);
- LOG.info(">>>>>>>>>JOB:STATUS:" + status);
- return status.contains("FAILED");
+ return containsStatus(getUser(), project, jobUUID, "FAILED");
}
});
}
diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario11/SigTermSimulationJobScenario11IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario11/SigTermSimulationJobScenario11IntTest.java
index 9e63eb4776..a9d56aaf6f 100644
--- a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario11/SigTermSimulationJobScenario11IntTest.java
+++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario11/SigTermSimulationJobScenario11IntTest.java
@@ -13,11 +13,12 @@
import org.junit.Test;
import org.junit.rules.Timeout;
+import com.mercedesbenz.sechub.commons.model.TrafficLight;
import com.mercedesbenz.sechub.commons.pds.data.PDSJobStatusState;
import com.mercedesbenz.sechub.integrationtest.api.IntegrationTestSetup;
-import com.mercedesbenz.sechub.integrationtest.api.TestAPI;
import com.mercedesbenz.sechub.integrationtest.api.TestProject;
import com.mercedesbenz.sechub.integrationtest.api.TestSecHubJobInfoForUserListPage;
+import com.mercedesbenz.sechub.integrationtest.api.TestUser;
/**
* Integration tests to check SIGTERM handling operations work
@@ -42,19 +43,23 @@ public class SigTermSimulationJobScenario11IntTest {
*
* The PDS job will run in background without stop. When new SecHub server comes
* up (we simulate by turning off termination flag in integration test mode...)
- * the result from PDS are reused (if the PDS job is not already done SecHub
- * will wait and reuse - so we have no race condition problems here)
+ * the result from PDS is reused (if the PDS job is not already done SecHub will
+ * wait and reuse - so we have no race condition problems here)
*/
public void simulate_SecHub_SIGTERM_handling_leads_to_restart_of_SecHub_job_and_reuse_of_pds_job() {
+ TestUser user = USER_1;
+ boolean resetCalled = false;
try {
/* @formatter:off */
/* check preconditions */
- assertThat(TestAPI.isSecHubTerminating()).describedAs("Ensure the server is not already terminating").isFalse();
+ assertThat(isSecHubTerminating()).
+ describedAs("Ensure the server is not already terminating").
+ isFalse();
- /* prepare */
- UUID sechubJobUUD = as(USER_1).triggerAsyncPDSCodeScanWithDifferentDataSections(project);
+ /* prepare 1 */
+ UUID sechubJobUUD = as(user).triggerAsyncPDSCodeScanWithWantedTrafficLightResult(project, TrafficLight.YELLOW);
waitForJobRunning(project, sechubJobUUD);
UUID pdsJobUUID = waitForFirstPDSJobOfSecHubJobAndReturnPDSJobUUID(sechubJobUUD);
waitForPDSJobInState(PDSJobStatusState.RUNNING, 2000,100,pdsJobUUID,true);
@@ -67,29 +72,48 @@ public void simulate_SecHub_SIGTERM_handling_leads_to_restart_of_SecHub_job_and_
assertPDSJobStatus(pdsJobUUID).isInState(PDSJobStatusState.RUNNING); // pds job is still running
// fetch last user job - must be the one we have created here...
- TestSecHubJobInfoForUserListPage jobInfo = as(USER_1).
+ TestSecHubJobInfoForUserListPage jobInfo = as(user).
fetchUserJobInfoListOneEntryOrNull(project);
- assertUserJobInfo(jobInfo).
+
+ assertUserJobInfo(jobInfo).
hasJobInfoFor(sechubJobUUD).
withEndedTimeStampNotNull().
withExecutionResult("NONE"); // no result
- /* execute 2 - we simulate here a new server start - means without termination flag set -> job processing will start again and processes old job */
+ /* prepare 2 - we simulate here a new server start - means without termination
+ * flag set -> job processing will start again and processes old job
+ * like a new server would do
+ */
resetSecHubTerminationService();
+ resetCalled=true;
+
+ /* test 2 - check suspended report will resume and be done automatically */
+ waitForJobDone(project, sechubJobUUD, 15, true);
- /* test 2 */
- waitForJobDone(project, sechubJobUUD, 5, true);
+ /* test 3 - check report is as expected */
+ String report = as(user).getJobReport(project, sechubJobUUD);
+ assertReportUnordered(report).
+ hasTrafficLight(TrafficLight.YELLOW).
+ finding().description("i am a medium error").isContained();
+
+ /* test 4 - check only ONE PDS job was used */
List relatedPSjobUUIDs = fetchAllPDSJobUUIDsForSecHubJob(sechubJobUUD);
assertThat(relatedPSjobUUIDs).
describedAs("Check that ONE PDS job has been reused by SecHub job which was suspended and now restarted").
hasSize(1).
contains(pdsJobUUID);
+
/* @formatter:on */
} finally {
- // we always reset the termination service to avoid cross site effects on other
- // tests or on restart of this test
- TestAPI.resetSecHubTerminationService();
+ if (!resetCalled) {
+ // IMPORTANT:
+ // -----------
+ // we MUST ensure termination service reset is done to avoid cross site effects
+ // on other
+ // tests when reset was not called in former try block!
+ resetSecHubTerminationService();
+ }
}
}
}
\ No newline at end of file
diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario4/JobUsecasesEventTraceScenario4IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario4/JobUsecasesEventTraceScenario4IntTest.java
index ae9ae5db8d..7464f89c65 100644
--- a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario4/JobUsecasesEventTraceScenario4IntTest.java
+++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario4/JobUsecasesEventTraceScenario4IntTest.java
@@ -120,7 +120,7 @@ public void UC_ADMIN_RESTARTS_JOB_HARD__simulate_jvm_crash_but_product_results_i
to("com.mercedesbenz.sechub.domain.scan.ScanService").
/* 7 */
syncEvent(MessageID.REQUEST_SCHEDULER_JOB_STATUS).
- from("com.mercedesbenz.sechub.domain.scan.ScanProgressMonitor").
+ from("com.mercedesbenz.sechub.domain.scan.ScanProgressStateFetcher").
to("com.mercedesbenz.sechub.domain.schedule.job.SchedulerJobStatusRequestHandler").
/* 8 */
asyncEvent(MessageID.JOB_DONE).
@@ -182,7 +182,7 @@ public void UC_ADMIN_RESTARTS_JOB_HARD__simulate_jvm_crash_no_product_results_in
to("com.mercedesbenz.sechub.domain.scan.ScanService").
/* 6 */
syncEvent(MessageID.REQUEST_SCHEDULER_JOB_STATUS).
- from("com.mercedesbenz.sechub.domain.scan.ScanProgressMonitor").
+ from("com.mercedesbenz.sechub.domain.scan.ScanProgressStateFetcher").
to("com.mercedesbenz.sechub.domain.schedule.job.SchedulerJobStatusRequestHandler").
/* 7 */
asyncEvent(MessageID.JOB_DONE).
@@ -279,7 +279,7 @@ public void UC_ADMIN_RESTARTS_JOB__simulate_jvm_crash_but_product_results_in_db(
to("com.mercedesbenz.sechub.domain.scan.ScanService").
/* 5 */
syncEvent(MessageID.REQUEST_SCHEDULER_JOB_STATUS).
- from("com.mercedesbenz.sechub.domain.scan.ScanProgressMonitor").
+ from("com.mercedesbenz.sechub.domain.scan.ScanProgressStateFetcher").
to("com.mercedesbenz.sechub.domain.schedule.job.SchedulerJobStatusRequestHandler").
/* 6 */
asyncEvent(MessageID.JOB_DONE).
@@ -338,7 +338,7 @@ public void UC_ADMIN_RESTARTS_JOB__simulate_jvm_crash_no_product_results_in_db_u
to("com.mercedesbenz.sechub.domain.scan.ScanService").
/* 5 */
syncEvent(MessageID.REQUEST_SCHEDULER_JOB_STATUS).
- from("com.mercedesbenz.sechub.domain.scan.ScanProgressMonitor").
+ from("com.mercedesbenz.sechub.domain.scan.ScanProgressStateFetcher").
to("com.mercedesbenz.sechub.domain.schedule.job.SchedulerJobStatusRequestHandler").
/* 6 */
asyncEvent(MessageID.JOB_DONE).
diff --git a/sechub-notification/src/main/java/com/mercedesbenz/sechub/domain/notification/superadmin/InformAdminsThatNewSchedulerInstanceHasBeenStartedNotificationService.java b/sechub-notification/src/main/java/com/mercedesbenz/sechub/domain/notification/superadmin/InformAdminsThatNewSchedulerInstanceHasBeenStartedNotificationService.java
index 8e40e082bc..e6a2a66693 100644
--- a/sechub-notification/src/main/java/com/mercedesbenz/sechub/domain/notification/superadmin/InformAdminsThatNewSchedulerInstanceHasBeenStartedNotificationService.java
+++ b/sechub-notification/src/main/java/com/mercedesbenz/sechub/domain/notification/superadmin/InformAdminsThatNewSchedulerInstanceHasBeenStartedNotificationService.java
@@ -11,6 +11,7 @@
import com.mercedesbenz.sechub.domain.notification.NotificationConfiguration;
import com.mercedesbenz.sechub.domain.notification.email.EmailService;
import com.mercedesbenz.sechub.domain.notification.email.MailMessageFactory;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.sharedkernel.Step;
import com.mercedesbenz.sechub.sharedkernel.messaging.ClusterMemberMessage;
@@ -22,7 +23,7 @@ public class InformAdminsThatNewSchedulerInstanceHasBeenStartedNotificationServi
private static final Logger LOG = LoggerFactory.getLogger(InformAdminsThatNewSchedulerInstanceHasBeenStartedNotificationService.class);
@Value("${sechub.notification.scheduler.startup.enabled:true}")
- @MustBeDocumented(scope = "administration", value = "When enabled, administrators will be informed by notification "
+ @MustBeDocumented(scope = DocumentationScopeConstants.SCOPE_ADMINISTRATION, value = "When enabled, administrators will be informed by notification "
+ "when new scheduler instances are started. " + "Those notifications will also contain information about potential zombie jobs.\n\n"
+ " When disabled, incoming events will be ignored and no notification sent.")
boolean notificationEnabled;
diff --git a/sechub-scan-product-sereco/src/main/java/com/mercedesbenz/sechub/domain/scan/product/sereco/SerecoProductResultTransformer.java b/sechub-scan-product-sereco/src/main/java/com/mercedesbenz/sechub/domain/scan/product/sereco/SerecoProductResultTransformer.java
index 4c20782c43..a456bb9d05 100644
--- a/sechub-scan-product-sereco/src/main/java/com/mercedesbenz/sechub/domain/scan/product/sereco/SerecoProductResultTransformer.java
+++ b/sechub-scan-product-sereco/src/main/java/com/mercedesbenz/sechub/domain/scan/product/sereco/SerecoProductResultTransformer.java
@@ -48,6 +48,7 @@
import com.mercedesbenz.sechub.sereco.metadata.SerecoWebEvidence;
import com.mercedesbenz.sechub.sereco.metadata.SerecoWebRequest;
import com.mercedesbenz.sechub.sereco.metadata.SerecoWebResponse;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
@@ -58,7 +59,7 @@ public class SerecoProductResultTransformer implements ReportProductResultTransf
SerecoFalsePositiveMarker falsePositiveMarker;
@Value("${sechub.feature.showProductResultLink:false}")
- @MustBeDocumented(scope = "administration", value = "Administrators can turn on this mode to allow product links in json and HTML output")
+ @MustBeDocumented(scope = DocumentationScopeConstants.SCOPE_ADMINISTRATION, value = "Administrators can turn on this mode to allow product links in json and HTML output")
boolean showProductLineResultLink;
private static final Logger LOG = LoggerFactory.getLogger(SerecoProductResultTransformer.class);
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/IntegrationTestSchedulerRestController.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/IntegrationTestSchedulerRestController.java
index 096296300c..9e96565e19 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/IntegrationTestSchedulerRestController.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/IntegrationTestSchedulerRestController.java
@@ -18,7 +18,7 @@
import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleCipherPoolCleanupService;
import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
-import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerStrategyFactory;
+import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerStrategyProvider;
import com.mercedesbenz.sechub.sharedkernel.APIConstants;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
@@ -40,7 +40,7 @@ public class IntegrationTestSchedulerRestController {
private IntegrationTestSchedulerService integrationTestSchedulerService;
@Autowired
- private SchedulerStrategyFactory schedulerStrategyFactory;
+ private SchedulerStrategyProvider schedulerStrategyProvider;
@Autowired
private SchedulerConfigService scheduleConfigService;
@@ -94,7 +94,7 @@ public void revertJobAsStillNotApproved(@PathVariable("sechubJobUUID") UUID sech
@RequestMapping(path = APIConstants.API_ANONYMOUS + "integrationtest/scheduler/strategy/{strategyId}", method = RequestMethod.PUT, produces = {
MediaType.APPLICATION_JSON_VALUE })
public void setSchedulerStrategy(@PathVariable("strategyId") String strategyId) {
- schedulerStrategyFactory.setStrategyIdentifier(strategyId);
+ schedulerStrategyProvider.setStrategyIdentifier(strategyId);
}
@RequestMapping(path = APIConstants.API_ANONYMOUS
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerService.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerService.java
index 2a5221b96f..b9b060c77f 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerService.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerService.java
@@ -21,8 +21,7 @@
import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJobMessagesSupport;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
-import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerStrategy;
-import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerStrategyFactory;
+import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerNextJobResolver;
/**
* This service is only responsible to mark next {@link ScheduleSecHubJob} to
@@ -41,9 +40,7 @@ public class ScheduleJobMarkerService {
SecHubJobRepository jobRepository;
@Autowired
- SchedulerStrategyFactory schedulerStrategyFactory;
-
- private SchedulerStrategy schedulerStrategy;
+ SchedulerNextJobResolver nextJobResolver;
private ScheduleSecHubJobMessagesSupport jobMessageSupport = new ScheduleSecHubJobMessagesSupport();
@@ -54,27 +51,29 @@ public class ScheduleJobMarkerService {
@Transactional
public ScheduleSecHubJob markNextJobToExecuteByThisInstance() {
- schedulerStrategy = schedulerStrategyFactory.build();
-
if (LOG.isTraceEnabled()) {
LOG.trace("Trigger execution of next job started");
}
- UUID nextJobId = schedulerStrategy.nextJobId();
+ UUID nextJobId = nextJobResolver.resolveNextJob();
if (nextJobId == null) {
return null;
}
- Optional secHubJobOptional = jobRepository.getJob(nextJobId);
+ Optional secHubJobOptional = jobRepository.getJobWhenExecutable(nextJobId);
if (!secHubJobOptional.isPresent()) {
- if (LOG.isTraceEnabled()) {
- LOG.trace("No job found.");
- }
+ LOG.warn("Did not found job for next job UUID:{}", nextJobId);
return null;
}
ScheduleSecHubJob secHubJob = secHubJobOptional.get();
- secHubJob.setExecutionState(ExecutionState.STARTED);
- secHubJob.setStarted(LocalDateTime.now());
+ ExecutionState state = secHubJob.getExecutionState();
+
+ if (ExecutionState.SUSPENDED.equals(state)) {
+ secHubJob.setExecutionState(ExecutionState.RESUMING);
+ } else {
+ secHubJob.setExecutionState(ExecutionState.STARTED);
+ secHubJob.setStarted(LocalDateTime.now());
+ }
return jobRepository.save(secHubJob);
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleResumeJobService.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleResumeJobService.java
new file mode 100644
index 0000000000..ed7fbbb7a9
--- /dev/null
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/ScheduleResumeJobService.java
@@ -0,0 +1,49 @@
+package com.mercedesbenz.sechub.domain.schedule;
+
+import java.util.UUID;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
+import com.mercedesbenz.sechub.sharedkernel.SecHubEnvironment;
+import com.mercedesbenz.sechub.sharedkernel.Step;
+import com.mercedesbenz.sechub.sharedkernel.messaging.DomainMessage;
+import com.mercedesbenz.sechub.sharedkernel.messaging.DomainMessageFactory;
+import com.mercedesbenz.sechub.sharedkernel.messaging.DomainMessageService;
+import com.mercedesbenz.sechub.sharedkernel.messaging.IsSendingAsyncMessage;
+import com.mercedesbenz.sechub.sharedkernel.messaging.JobMessage;
+import com.mercedesbenz.sechub.sharedkernel.messaging.MessageDataKeys;
+import com.mercedesbenz.sechub.sharedkernel.messaging.MessageID;
+import com.mercedesbenz.sechub.sharedkernel.usecases.other.UseCaseSystemHandlesSIGTERM;
+
+@Service
+public class ScheduleResumeJobService {
+
+ @Autowired
+ DomainMessageService eventBusService;
+
+ @Autowired
+ SecHubEnvironment sechubEnvironment;
+
+ @UseCaseSystemHandlesSIGTERM(@Step(number = 8, name = "Resume", description = "Triggers restart of job"))
+ public void resume(ScheduleSecHubJob next) {
+ triggerJobRestartRequest(next.getUUID());
+ }
+
+ @IsSendingAsyncMessage(MessageID.REQUEST_JOB_RESTART)
+ private void triggerJobRestartRequest(UUID jobUUID) {
+
+ JobMessage message = new JobMessage();
+ message.setJobUUID(jobUUID);
+
+ // we do NOT use REQUEST_JOB_RESTART_HARD ! Resaon: hard would destroy all
+ // former data!
+ DomainMessage restartJobRequest = DomainMessageFactory.createEmptyRequest(MessageID.REQUEST_JOB_RESTART);
+ restartJobRequest.set(MessageDataKeys.JOB_RESTART_DATA, message);
+ restartJobRequest.set(MessageDataKeys.ENVIRONMENT_BASE_URL, sechubEnvironment.getServerBaseUrl());
+
+ eventBusService.sendAsynchron(restartJobRequest);
+ }
+
+}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/SchedulerJobBatchTriggerService.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/SchedulerJobBatchTriggerService.java
index 5a21bce250..e9f33192e0 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/SchedulerJobBatchTriggerService.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/SchedulerJobBatchTriggerService.java
@@ -14,14 +14,17 @@
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
+import com.mercedesbenz.sechub.commons.model.job.ExecutionState;
import com.mercedesbenz.sechub.domain.schedule.config.SchedulerConfigService;
import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.sharedkernel.Step;
import com.mercedesbenz.sechub.sharedkernel.cluster.ClusterEnvironmentService;
import com.mercedesbenz.sechub.sharedkernel.logging.AlertLogService;
import com.mercedesbenz.sechub.sharedkernel.monitoring.SystemMonitorService;
import com.mercedesbenz.sechub.sharedkernel.usecases.job.UseCaseSchedulerStartsJob;
+import com.mercedesbenz.sechub.sharedkernel.usecases.other.UseCaseSystemHandlesSIGTERM;
import jakarta.annotation.PostConstruct;
@@ -86,6 +89,9 @@ public class SchedulerJobBatchTriggerService {
@Autowired
SchedulerTerminationService schedulerTerminationService;
+ @Autowired
+ ScheduleResumeJobService resumeJobService;
+
@PostConstruct
protected void postConstruct() {
// show info about delay values in log (once)
@@ -95,10 +101,11 @@ protected void postConstruct() {
// default 10 seconds delay and 5 seconds initial
@MustBeDocumented(value = "Job scheduling is triggered by a cron job operation - default is 10 seconds to delay after last execution. "
+ "For initial delay " + DEFAULT_INITIAL_DELAY_MILLIS
- + " milliseconds are defined. It can be configured differently. This is useful when you need to startup a cluster. Simply change the initial delay values in to allow the cluster to startup.", scope = "schedule")
+ + " milliseconds are defined. It can be configured differently. This is useful when you need to startup a cluster. Simply change the initial delay values in to allow the cluster to startup.", scope = DocumentationScopeConstants.SCOPE_SCHEDULE)
@Scheduled(initialDelayString = "${sechub.config.trigger.nextjob.initialdelay:" + DEFAULT_INITIAL_DELAY_MILLIS
+ "}", fixedDelayString = "${sechub.config.trigger.nextjob.delay:" + DEFAULT_FIXED_DELAY_MILLIS + "}")
@UseCaseSchedulerStartsJob(@Step(number = 1, name = "Scheduling", description = "Fetches next schedule job from queue and trigger execution."))
+ @UseCaseSystemHandlesSIGTERM(@Step(number = 7, name = "Scheduling", description = "When a suspended job exists which shall be executed again, it will be marked as SUSPENDED and a restart will be triggered"))
public void triggerExecutionOfNextJob() {
if (LOG.isTraceEnabled()) {
/* NOSONAR */LOG.trace("Trigger execution of next job started. Environment: {}", environmentService.getEnvironment());
@@ -136,6 +143,12 @@ public void triggerExecutionOfNextJob() {
if (next == null) {
return;
}
+ if (ExecutionState.RESUMING.equals(next.getExecutionState())) {
+ LOG.info("Resuming job: {}", next.getUUID());
+ resumeJobService.resume(next);
+ return;
+ }
+
try {
launcherService.executeJob(next);
} catch (Exception e) {
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/encryption/ScheduleCipherPoolCleanupService.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/encryption/ScheduleCipherPoolCleanupService.java
index f1e935b0c8..268a5968a4 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/encryption/ScheduleCipherPoolCleanupService.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/encryption/ScheduleCipherPoolCleanupService.java
@@ -40,7 +40,7 @@ public class ScheduleCipherPoolCleanupService {
@UseCaseEncryptionCleanup(@Step(number = 1, name = "Schedule cipher pool data cleanup", description = DESCRIPTION))
@UseCaseScheduleAutoCleanExecution(@Step(number = 3, name = "Schedule cipher pool data cleanup", description = DESCRIPTION))
public void cleanupCipherPoolDataIfNecessaryAndPossible() {
- LOG.debug("Encryption pool cleanup check");
+ LOG.trace("Encryption pool cleanup check");
/* check clean up possible */
if (outdatedEncryptionPoolSupport.isOutdatedEncryptionPoolPossibleInCluster()) {
@@ -74,11 +74,11 @@ public void cleanupCipherPoolDataIfNecessaryAndPossible() {
}
private void startEncryptionCleanup(List allPoolData, ScheduleCipherPoolData latestPoolDataFromDatabase) {
- LOG.debug("Encryption pool cleanup start");
+ LOG.trace("Encryption pool cleanup start");
List poolDataToRemove = calculatePoolDataToRemove(allPoolData, latestPoolDataFromDatabase);
if (poolDataToRemove.isEmpty()) {
- LOG.debug("Found no pool data to remove");
+ LOG.trace("Found no pool data to remove");
return;
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryCustom.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryCustom.java
index 2b769ec44d..1d6b5c97a6 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryCustom.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryCustom.java
@@ -12,7 +12,14 @@
public interface SecHubJobRepositoryCustom {
- Optional getJob(UUID id);
+ /**
+ * Returns job when in executable - means execution state is either
+ * READY_TO_START or SUSPENDED.
+ *
+ * @param sechubJobUUID job uuid to search for
+ * @return job or null
+ */
+ Optional getJobWhenExecutable(UUID sechubJobUUID);
Optional nextJobIdToExecuteFirstInFirstOut(Set acceptedEncryptiondPoolIds);
@@ -20,6 +27,8 @@ public interface SecHubJobRepositoryCustom {
Optional nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(Set acceptedEncryptiondPoolIds);
+ Optional nextJobIdToExecuteSuspended(Set supportedPoolIds, long minumSuspendDurationInMilliseconds);
+
/**
* Fetches next jobs which have been canceled or have ended but have an
* encryption pool entry which is lower (means older) than the given one. The
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryImpl.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryImpl.java
index 7af121d6a5..673db8e58f 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryImpl.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryImpl.java
@@ -3,6 +3,8 @@
import static com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob.*;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -20,9 +22,11 @@ public class SecHubJobRepositoryImpl implements SecHubJobRepositoryCustom {
private static final String PARAM_UUID = "p_uuid";
private static final String PARAM_EXECUTION_STATE = "p_exec_state";
+ private static final String PARAM_EXECUTION_STATES = "p_exec_states";
private static final String PARAM_EXECUTION_STATE_SUB = "p_sub_exec_state";
private static final String PARAM_ENCRYPTION_POOL_ID = "p_encrypt_pool_id";
private static final String PARAM_ENCRYPTION_POOL_IDS = "p_encrypt_pool_ids";
+ private static final String PARAM_MINIMUM_JOB_ENDED = "p_min_job_ended";
/* @formatter:off */
static final String JPQL_STRING_SELECT_BY_EXECUTION_STATE =
@@ -33,7 +37,7 @@ public class SecHubJobRepositoryImpl implements SecHubJobRepositoryCustom {
static final String JPQL_STRING_SELECT_BY_JOB_ID =
"select j from " + CLASS_NAME + " j" +
- " where j." + PROPERTY_EXECUTION_STATE + " = :" + PARAM_EXECUTION_STATE +
+ " where j." + PROPERTY_EXECUTION_STATE + " in :" + PARAM_EXECUTION_STATES +
" and j." + PROPERTY_UUID + " = :" + PARAM_UUID;
static final String SUB_JPQL_STRING_SELECT_PROJECTS_WITH_RUNNING_JOBS =
@@ -76,6 +80,12 @@ public class SecHubJobRepositoryImpl implements SecHubJobRepositoryCustom {
static final String JPQL_STRING_FETCH_ALL_USED_ENCRYPTION_POOL_IDS_IN_JOBS =
"select DISTINCT j."+PROPERTY_ENCRYPTION_POOL_ID+" from " + CLASS_NAME + " j";
+ static final String JPQL_STRING_SELECT_LATEST_SUSPENDED_JOB_OLDER_THAN_GIVEN_TIME =
+ "select j from " + CLASS_NAME + " j" +
+ " where j." + PROPERTY_EXECUTION_STATE + " = " + ExecutionState.SUSPENDED +
+ " and j."+ PROPERTY_ENCRYPTION_POOL_ID +" in (:"+PARAM_ENCRYPTION_POOL_IDS+")"+
+ " and j." + PROPERTY_ENDED + " <:"+PARAM_MINIMUM_JOB_ENDED +
+ " order by " + PROPERTY_CREATED;
/* @formatter:on */
@@ -85,9 +95,9 @@ public class SecHubJobRepositoryImpl implements SecHubJobRepositoryCustom {
private EntityManager em;
@Override
- public Optional getJob(UUID id) {
+ public Optional getJobWhenExecutable(UUID id) {
Query query = em.createQuery(JPQL_STRING_SELECT_BY_JOB_ID);
- query.setParameter(PARAM_EXECUTION_STATE, ExecutionState.READY_TO_START);
+ query.setParameter(PARAM_EXECUTION_STATES, Set.of(ExecutionState.READY_TO_START, ExecutionState.SUSPENDED));
query.setParameter(PARAM_UUID, id);
query.setMaxResults(1);
query.setLockMode(LockModeType.OPTIMISTIC_FORCE_INCREMENT);
@@ -165,4 +175,17 @@ public List collectAllUsedEncryptionPoolIdsInsideJobs() {
return query.getResultList();
}
+ @Override
+ public Optional nextJobIdToExecuteSuspended(Set supportedPoolIds, long minumSuspendDurationInMilliseconds) {
+ LocalDateTime minimumJobEndedDateTime = LocalDateTime.now().minus(minumSuspendDurationInMilliseconds, ChronoUnit.MILLIS);
+
+ Query query = em.createQuery(JPQL_STRING_SELECT_LATEST_SUSPENDED_JOB_OLDER_THAN_GIVEN_TIME);
+ query.setParameter(PARAM_ENCRYPTION_POOL_IDS, supportedPoolIds);
+ query.setParameter(PARAM_MINIMUM_JOB_ENDED, minimumJobEndedDateTime);
+ query.setMaxResults(1);
+ query.setLockMode(LockModeType.OPTIMISTIC_FORCE_INCREMENT);
+
+ return getUUIDFromJob(typedQuerySupport.getSingleResultAsOptional(query));
+ }
+
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategy.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategy.java
index a56b4d4bfb..b7eaf0afb2 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategy.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategy.java
@@ -8,7 +8,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
@Component
@@ -17,24 +16,14 @@ public class FirstComeFirstServeSchedulerStrategy implements SchedulerStrategy {
@Autowired
public SecHubJobRepository jobRepository;
- @Autowired
- ScheduleEncryptionService encryptionService;
-
@Override
- public SchedulerStrategyId getSchedulerId() {
+ public SchedulerStrategyId getSchedulerStrategyId() {
return SchedulerStrategyId.FIRST_COME_FIRST_SERVE;
}
@Override
- public UUID nextJobId() {
- Set supportedPoolIds = encryptionService.getCurrentEncryptionPoolIds();
-
- Optional nextJob = jobRepository.nextJobIdToExecuteFirstInFirstOut(supportedPoolIds);
- if (!nextJob.isPresent()) {
- return null;
- }
-
- return nextJob.get();
+ public Optional nextJobId(Set supportedEncryptionPoolIds) {
+ return jobRepository.nextJobIdToExecuteFirstInFirstOut(supportedEncryptionPoolIds);
}
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy.java
index 67125fdee1..f432bf8ab6 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy.java
@@ -8,7 +8,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
/**
@@ -54,24 +53,14 @@ public class OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy implements Sc
@Autowired
SecHubJobRepository jobRepository;
- @Autowired
- ScheduleEncryptionService encryptionService;
-
@Override
- public SchedulerStrategyId getSchedulerId() {
+ public SchedulerStrategyId getSchedulerStrategyId() {
return SchedulerStrategyId.ONE_SCAN_PER_PROJECT_AND_MODULE_GROUP;
}
@Override
- public UUID nextJobId() {
- Set supportedPoolIds = encryptionService.getCurrentEncryptionPoolIds();
-
- Optional nextJob = jobRepository.nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(supportedPoolIds);
- if (!nextJob.isPresent()) {
- return null;
- }
-
- return nextJob.get();
+ public Optional nextJobId(Set supportedEncryptionPoolIds) {
+ return jobRepository.nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(supportedEncryptionPoolIds);
}
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategy.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategy.java
index cb76a706f7..c438c80cba 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategy.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategy.java
@@ -8,7 +8,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
@Component
@@ -17,24 +16,14 @@ public class OnlyOneScanPerProjectAtSameTimeStrategy implements SchedulerStrateg
@Autowired
SecHubJobRepository jobRepository;
- @Autowired
- ScheduleEncryptionService encryptionService;
-
@Override
- public SchedulerStrategyId getSchedulerId() {
+ public SchedulerStrategyId getSchedulerStrategyId() {
return SchedulerStrategyId.ONE_SCAN_PER_PROJECT;
}
@Override
- public UUID nextJobId() {
- Set supportedPoolIds = encryptionService.getCurrentEncryptionPoolIds();
-
- Optional nextJob = jobRepository.nextJobIdToExecuteForProjectNotYetExecuted(supportedPoolIds);
- if (!nextJob.isPresent()) {
- return null;
- }
-
- return nextJob.get();
+ public Optional nextJobId(Set supportedEncryptionPoolIds) {
+ return jobRepository.nextJobIdToExecuteForProjectNotYetExecuted(supportedEncryptionPoolIds);
}
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerNextJobResolver.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerNextJobResolver.java
new file mode 100644
index 0000000000..527b62c248
--- /dev/null
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerNextJobResolver.java
@@ -0,0 +1,79 @@
+package com.mercedesbenz.sechub.domain.schedule.strategy;
+
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
+import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
+import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
+
+@Component
+public class SchedulerNextJobResolver {
+
+ private static final long DEFAULT_MIN_SUSPED_DURATION = 1000;
+
+ @Autowired
+ ScheduleEncryptionService encryptionService;
+
+ @Autowired
+ public SecHubJobRepository jobRepository;
+
+ @Autowired
+ SchedulerStrategyProvider schedulerStrategyProvider;
+
+ @Value("${sechub.scheduler.nextjob.suspend.miniumumduration.milliseconds:" + DEFAULT_MIN_SUSPED_DURATION + "}")
+ @MustBeDocumented(scope = DocumentationScopeConstants.SCOPE_SCHEDULE, value = """
+ The scheduler automatically restarts the next suspended jobs, regardless of the defined schedule strategy.
+ This is done to get suspended jobs of another shut down instance back up and running as quickly as possible.
+
+ To avoid suspended jobs being restarted too quickly, you can use this value to set the minimum time that must pass
+ before the next suspended job can be restarted. The value is defined in milliseconds.
+
+ The (previous) end date of the suspended job is used. For example, this value is important for
+ K8s redeployment, because the servers that have not yet been updated should not immediately continue with the
+ suspended jobs - they will also be shut down soon and would suspend the restarted jobs again...
+ """)
+ long minimumSuspendDurationInMilliseconds;
+
+ /**
+ * Resolves next job by given strategy. But before strategy is used, suspended
+ * jobs are fetched - if they are not too young (to avoid race conditions when
+ * we we have an ongoing deployment).
+ *
+ * @param strategy strategy to use when there are no suspended jobs
+ * @return uuid of job or null
if there is no job to execute
+ */
+ public UUID resolveNextJob() {
+
+ SchedulerStrategy schedulerStrategy = schedulerStrategyProvider.build();
+
+ Set supportedPoolIds = encryptionService.getCurrentEncryptionPoolIds();
+
+ if (supportedPoolIds == null || supportedPoolIds.isEmpty()) {
+ return null;
+ }
+ /*
+ * we always fetch the next possible suspended job - no matter which kind of
+ * strategy
+ */
+ Optional nextSuspendedJob = jobRepository.nextJobIdToExecuteSuspended(supportedPoolIds, minimumSuspendDurationInMilliseconds);
+ if (nextSuspendedJob.isPresent()) {
+ return nextSuspendedJob.get();
+ }
+
+ /* No suspended jobs available - use strategy to find next one */
+ Optional nextJob = schedulerStrategy.nextJobId(supportedPoolIds);
+ if (!nextJob.isPresent()) {
+ return null;
+ }
+
+ return nextJob.get();
+ }
+
+}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategy.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategy.java
index 43ca789803..e2f524cb9c 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategy.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategy.java
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.domain.schedule.strategy;
+import java.util.Optional;
+import java.util.Set;
import java.util.UUID;
public interface SchedulerStrategy {
- public SchedulerStrategyId getSchedulerId();
+ public SchedulerStrategyId getSchedulerStrategyId();
- public UUID nextJobId();
+ public Optional nextJobId(Set supportedEncryptionPoolIds);
}
diff --git a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyFactory.java b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyProvider.java
similarity index 97%
rename from sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyFactory.java
rename to sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyProvider.java
index 28928314a0..bd461e5a82 100644
--- a/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyFactory.java
+++ b/sechub-schedule/src/main/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyProvider.java
@@ -10,9 +10,9 @@
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
@Component
-public class SchedulerStrategyFactory {
+public class SchedulerStrategyProvider {
- private static final Logger LOG = LoggerFactory.getLogger(SchedulerStrategyFactory.class);
+ private static final Logger LOG = LoggerFactory.getLogger(SchedulerStrategyProvider.class);
@Autowired
FirstComeFirstServeSchedulerStrategy firstComeFirstServeStrategy;
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/JobCreator.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/JobCreator.java
index e36d04f339..17635683dd 100644
--- a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/JobCreator.java
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/JobCreator.java
@@ -86,6 +86,11 @@ public JobCreator created(LocalDateTime dateTime) {
return this;
}
+ public JobCreator ended(LocalDateTime dateTime) {
+ job.ended = dateTime;
+ return this;
+ }
+
public JobCreator project(String projectId) {
job.projectId = projectId;
return this;
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryDBTest.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryDBTest.java
index 3f33f5b16f..ed09a4021f 100644
--- a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryDBTest.java
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/job/SecHubJobRepositoryDBTest.java
@@ -27,6 +27,7 @@
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.EnumSource.Mode;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
@@ -628,6 +629,128 @@ void delete_job_with_data_deletes_data() {
assertNull(result);
}
+ /* -------------------------------------------------------------------------- */
+ /* ------------------ next job to execute suspended --------------- */
+ /* -------------------------------------------------------------------------- */
+
+ @ParameterizedTest
+ @EnumSource(mode = Mode.EXCLUDE, value = ExecutionState.class, names = "SUSPENDED")
+ void custom_query_nextJobIdToExecuteSuspended_1000_ms_duration_no_suspended_exists_but_only_one_job_in_other_state(ExecutionState state) {
+
+ /* prepare */
+ jobCreator.being(state).ended(LocalDateTime.now().minusSeconds(2)).create();
+
+ /* execute */
+ Optional uuid = jobRepository.nextJobIdToExecuteSuspended(FIRST_ENCRYPTION_POOL_ENTRY_ONLY, 1000L);
+
+ /* test */
+ assertFalse(uuid.isPresent());
+ }
+
+ @Test
+ void custom_query_nextJobIdToExecuteSuspended_2000_ms_duration_one_suspended_exist_but_only_1_second_so_nothing_returned() {
+ /* prepare */
+ jobCreator.being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(1)).create();
+
+ /* execute */
+ Optional uuid = jobRepository.nextJobIdToExecuteSuspended(FIRST_ENCRYPTION_POOL_ENTRY_ONLY, 2000L);
+
+ /* test */
+ assertFalse(uuid.isPresent());
+ }
+
+ @Test
+ void custom_query_nextJobIdToExecuteSuspended_1000_ms_duration_one_suspended_exist_older_1_second_returned() {
+ /* prepare */
+ ScheduleSecHubJob newJob = jobCreator.being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(2)).create();
+
+ /* execute */
+ Optional uuid = jobRepository.nextJobIdToExecuteSuspended(FIRST_ENCRYPTION_POOL_ENTRY_ONLY, 1000L);
+
+ /* test */
+ assertTrue(uuid.isPresent());
+ assertEquals(newJob.getUUID(), uuid.get());
+ }
+
+ @Test
+ void custom_query_nextJobIdToExecuteSuspended_1000_ms_duration_three_suspended_exist_both_older_1_second_older_returned() {
+ /* prepare */
+ jobCreator.created(LocalDateTime.now().minusSeconds(5)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(4)).create();
+ ScheduleSecHubJob newJob2 = jobCreator.created(LocalDateTime.now().minusSeconds(14)).being(ExecutionState.SUSPENDED)
+ .ended(LocalDateTime.now().minusSeconds(3)).create();
+ jobCreator.created(LocalDateTime.now().minusSeconds(4)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(2)).create();
+
+ /* execute */
+ Optional uuid = jobRepository.nextJobIdToExecuteSuspended(FIRST_ENCRYPTION_POOL_ENTRY_ONLY, 1000L);
+
+ /* test */
+ assertTrue(uuid.isPresent());
+ assertEquals(newJob2.getUUID(), uuid.get());
+ }
+
+ @Test
+ void custom_query_nextJobIdToExecuteSuspended_3000_ms_duration_three_suspended_exist_but_olders_only_2_second_returns_last_created_because_latest_suspended() {
+ /* prepare */
+ jobCreator.created(LocalDateTime.now().minusSeconds(15)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(2)).create();
+ ScheduleSecHubJob newJob2 = jobCreator.created(LocalDateTime.now().minusSeconds(14)).being(ExecutionState.SUSPENDED)
+ .ended(LocalDateTime.now().minusSeconds(3)).create();
+ jobCreator.created(LocalDateTime.now().minusSeconds(16)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(2)).create();
+
+ /* execute */
+ Optional uuid = jobRepository.nextJobIdToExecuteSuspended(FIRST_ENCRYPTION_POOL_ENTRY_ONLY, 3000L);
+
+ /* test */
+ assertTrue(uuid.isPresent());
+ assertEquals(newJob2.getUUID(), uuid.get());
+ }
+
+ @Test
+ void custom_query_nextJobIdToExecuteSuspended_5000_ms_duration_three_suspended_exist_both_younger_5_seconds_none_returned() {
+ /* prepare */
+ jobCreator.created(LocalDateTime.now().minusSeconds(5)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(4)).create();
+ jobCreator.created(LocalDateTime.now().minusSeconds(14)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(3)).create();
+ jobCreator.created(LocalDateTime.now().minusSeconds(4)).being(ExecutionState.SUSPENDED).ended(LocalDateTime.now().minusSeconds(2)).create();
+
+ /* execute */
+ Optional uuid = jobRepository.nextJobIdToExecuteSuspended(FIRST_ENCRYPTION_POOL_ENTRY_ONLY, 5000L);
+
+ /* test */
+ assertFalse(uuid.isPresent());
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* ------------------------ */
+ /* -------------------------------------------------------------------------- */
+
+ @ParameterizedTest
+ @EnumSource(value = ExecutionState.class, names = { "READY_TO_START", "SUSPENDED" }, mode = Mode.EXCLUDE)
+ void custom_query_getJob_returns_not_created_job_because_not_in_accepted_state(ExecutionState state) {
+ /* prepare */
+ ScheduleSecHubJob created = jobCreator.created(LocalDateTime.now().minusSeconds(5)).being(state).ended(LocalDateTime.now().minusSeconds(4)).create();
+
+ /* execute */
+ Optional result = jobRepository.getJobWhenExecutable(created.getUUID());
+
+ /* test */
+ assertFalse(result.isPresent());
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = ExecutionState.class, names = { "READY_TO_START", "SUSPENDED" }, mode = Mode.INCLUDE)
+ void custom_query_getJob_returns_created_job_because_in_accepted_state(ExecutionState state) {
+ /* prepare */
+ ScheduleSecHubJob created = jobCreator.created(LocalDateTime.now().minusSeconds(5)).being(state).ended(LocalDateTime.now().minusSeconds(4)).create();
+
+ /* execute */
+ Optional result = jobRepository.getJobWhenExecutable(created.getUUID());
+
+ /* test */
+ assertTrue(result.isPresent());
+ assertEquals(created, result.get());
+ }
+
+ /* -------------------------------------------------------------------------- */
+
@Test
void custom_query_nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted() {
/* prepare */
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategyTest.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategyTest.java
index 6fa219c6ef..52916eca21 100644
--- a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategyTest.java
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/FirstComeFirstServeSchedulerStrategyTest.java
@@ -12,7 +12,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
class FirstComeFirstServeSchedulerStrategyTest {
@@ -20,33 +19,29 @@ class FirstComeFirstServeSchedulerStrategyTest {
private FirstComeFirstServeSchedulerStrategy strategyToTest;
private SecHubJobRepository jobRepository;
private UUID jobUUID;
- private ScheduleEncryptionService encryptionService;
@BeforeEach
void beforeEach() {
jobUUID = UUID.randomUUID();
jobRepository = mock(SecHubJobRepository.class);
- encryptionService = mock(ScheduleEncryptionService.class);
-
strategyToTest = new FirstComeFirstServeSchedulerStrategy();
strategyToTest.jobRepository = jobRepository;
- strategyToTest.encryptionService = encryptionService;
}
@Test
void nextJobId_calls_expected_query_method() {
/* prepare */
Set currentEncryptionPoolIds = Collections.emptySet();
- when(encryptionService.getCurrentEncryptionPoolIds()).thenReturn(currentEncryptionPoolIds);
when(jobRepository.nextJobIdToExecuteFirstInFirstOut(currentEncryptionPoolIds)).thenReturn(Optional.of(jobUUID));
/* execute */
- UUID result = strategyToTest.nextJobId();
+ Optional result = strategyToTest.nextJobId(currentEncryptionPoolIds);
/* test */
- assertEquals(jobUUID, result);
verify(jobRepository).nextJobIdToExecuteFirstInFirstOut(currentEncryptionPoolIds);
+ assertTrue(result.isPresent());
+ assertEquals(jobUUID, result.get());
}
}
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategyTest.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategyTest.java
index 681ce41f86..7416659115 100644
--- a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategyTest.java
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategyTest.java
@@ -12,7 +12,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
class OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategyTest {
@@ -20,32 +19,29 @@ class OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategyTest {
private OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy strategyToTest;
private SecHubJobRepository jobRepository;
private UUID jobUUID;
- private ScheduleEncryptionService encryptionService;
@BeforeEach
void beforeEach() {
jobUUID = UUID.randomUUID();
jobRepository = mock(SecHubJobRepository.class);
- encryptionService = mock(ScheduleEncryptionService.class);
-
strategyToTest = new OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy();
strategyToTest.jobRepository = jobRepository;
- strategyToTest.encryptionService = encryptionService;
}
@Test
void nextJobId_calls_expected_query_method() {
/* prepare */
- Set supportedPoolIds = Collections.emptySet();
- when(jobRepository.nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(supportedPoolIds)).thenReturn(Optional.of(jobUUID));
+ Set currentEncryptionPoolIds = Collections.emptySet();
+ when(jobRepository.nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(currentEncryptionPoolIds)).thenReturn(Optional.of(jobUUID));
/* execute */
- UUID result = strategyToTest.nextJobId();
+ Optional result = strategyToTest.nextJobId(currentEncryptionPoolIds);
/* test */
- assertEquals(jobUUID, result);
- verify(jobRepository).nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(supportedPoolIds);
+ verify(jobRepository).nextJobIdToExecuteForProjectAndModuleGroupNotYetExecuted(currentEncryptionPoolIds);
+ assertTrue(result.isPresent());
+ assertEquals(jobUUID, result.get());
}
}
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategyTest.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategyTest.java
index 5228496bb6..dfaa6333e2 100644
--- a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategyTest.java
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/OnlyOneScanPerProjectAtSameTimeStrategyTest.java
@@ -12,7 +12,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
class OnlyOneScanPerProjectAtSameTimeStrategyTest {
@@ -20,32 +19,29 @@ class OnlyOneScanPerProjectAtSameTimeStrategyTest {
private OnlyOneScanPerProjectAtSameTimeStrategy strategyToTest;
private SecHubJobRepository jobRepository;
private UUID jobUUID;
- private ScheduleEncryptionService encryptionService;
@BeforeEach
void beforeEach() {
jobUUID = UUID.randomUUID();
jobRepository = mock(SecHubJobRepository.class);
- encryptionService = mock(ScheduleEncryptionService.class);
-
strategyToTest = new OnlyOneScanPerProjectAtSameTimeStrategy();
strategyToTest.jobRepository = jobRepository;
- strategyToTest.encryptionService = encryptionService;
}
@Test
void nextJobId_calls_expected_query_method() {
/* prepare */
- Set set = Collections.emptySet();
- when(jobRepository.nextJobIdToExecuteForProjectNotYetExecuted(set)).thenReturn(Optional.of(jobUUID));
+ Set currentEncryptionPoolIds = Collections.emptySet();
+ when(jobRepository.nextJobIdToExecuteForProjectNotYetExecuted(currentEncryptionPoolIds)).thenReturn(Optional.of(jobUUID));
/* execute */
- UUID result = strategyToTest.nextJobId();
+ Optional result = strategyToTest.nextJobId(currentEncryptionPoolIds);
/* test */
- assertEquals(jobUUID, result);
- verify(jobRepository).nextJobIdToExecuteForProjectNotYetExecuted(set);
+ verify(jobRepository).nextJobIdToExecuteForProjectNotYetExecuted(currentEncryptionPoolIds);
+ assertTrue(result.isPresent());
+ assertEquals(jobUUID, result.get());
}
}
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerNextJobResolverTest.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerNextJobResolverTest.java
new file mode 100644
index 0000000000..6e3447098a
--- /dev/null
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerNextJobResolverTest.java
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.schedule.strategy;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleEncryptionService;
+import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
+
+class SchedulerNextJobResolverTest {
+
+ private SchedulerNextJobResolver resolverToTest;
+ private SecHubJobRepository jobRepository;
+ private UUID jobUUID;
+ private ScheduleEncryptionService encryptionService;
+ private SchedulerStrategyProvider schedulerStrategyProvider;
+ private SchedulerStrategy strategy;
+
+ @BeforeEach
+ void beforeEach() {
+ jobUUID = UUID.randomUUID();
+ jobRepository = mock(SecHubJobRepository.class);
+ schedulerStrategyProvider = mock(SchedulerStrategyProvider.class);
+ strategy = mock(SchedulerStrategy.class);
+
+ encryptionService = mock(ScheduleEncryptionService.class);
+
+ resolverToTest = new SchedulerNextJobResolver();
+ resolverToTest.jobRepository = jobRepository;
+ resolverToTest.encryptionService = encryptionService;
+ resolverToTest.schedulerStrategyProvider = schedulerStrategyProvider;
+
+ when(schedulerStrategyProvider.build()).thenReturn(strategy);
+ }
+
+ @Test
+ void resolveNextJob_uses_encryption_service_when_no_encryption_pool_ids_available_no_interaction_and_always_null() {
+ /* prepare */
+ Set currentEncryptionPoolIds = Set.of(); // empty...
+ when(encryptionService.getCurrentEncryptionPoolIds()).thenReturn(currentEncryptionPoolIds);
+ when(jobRepository.nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds))
+ .thenReturn(Optional.of(jobUUID));
+
+ /* execute */
+ UUID result = resolverToTest.resolveNextJob();
+
+ /* test */
+ verify(jobRepository, never()).nextJobIdToExecuteSuspended(anySet(), anyLong());
+ verify(strategy, never()).nextJobId(anySet());
+ assertEquals(null, result);
+ }
+
+ @Test
+ void resolveNextJob_uses_encryption_service_and_fetches_suspended_job() {
+ /* prepare */
+ Set currentEncryptionPoolIds = Set.of(1L);
+ when(encryptionService.getCurrentEncryptionPoolIds()).thenReturn(currentEncryptionPoolIds);
+ when(jobRepository.nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds))
+ .thenReturn(Optional.of(jobUUID));
+
+ /* execute */
+ UUID result = resolverToTest.resolveNextJob();
+
+ /* test */
+ verify(jobRepository).nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds);
+ verify(strategy, never()).nextJobId(currentEncryptionPoolIds);
+ assertEquals(jobUUID, result);
+ }
+
+ @Test
+ void resolveNextJob_uses_encryption_service_no_suspended_jobs_found_then_job_from_strategy_is_used() {
+ /* prepare */
+ Set currentEncryptionPoolIds = Set.of(1L);
+ when(encryptionService.getCurrentEncryptionPoolIds()).thenReturn(currentEncryptionPoolIds);
+ when(jobRepository.nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds))
+ .thenReturn(Optional.empty());
+ when(strategy.nextJobId(currentEncryptionPoolIds)).thenReturn(Optional.of(jobUUID));
+
+ /* execute */
+ UUID result = resolverToTest.resolveNextJob();
+
+ /* test */
+ verify(jobRepository).nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds);
+ verify(strategy).nextJobId(currentEncryptionPoolIds);
+ assertEquals(jobUUID, result);
+ }
+
+ @Test
+ void resolveNextJob_uses_encryption_service_and_fetches_no_suspended_jobs_when_found_job_from_strategy_is_used_but_also_not_found() {
+ /* prepare */
+ Set currentEncryptionPoolIds = Set.of(1L);
+ when(encryptionService.getCurrentEncryptionPoolIds()).thenReturn(currentEncryptionPoolIds);
+ when(jobRepository.nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds))
+ .thenReturn(Optional.empty());
+ when(strategy.nextJobId(currentEncryptionPoolIds)).thenReturn(Optional.empty());
+
+ /* execute */
+ UUID result = resolverToTest.resolveNextJob();
+
+ /* test */
+ verify(jobRepository).nextJobIdToExecuteSuspended(currentEncryptionPoolIds, resolverToTest.minimumSuspendDurationInMilliseconds);
+ verify(strategy).nextJobId(currentEncryptionPoolIds);
+ assertEquals(null, result);
+ }
+
+}
diff --git a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyFactoryTest.java b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyProviderTest.java
similarity index 96%
rename from sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyFactoryTest.java
rename to sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyProviderTest.java
index 5abb965096..a91766f956 100644
--- a/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyFactoryTest.java
+++ b/sechub-schedule/src/test/java/com/mercedesbenz/sechub/domain/schedule/strategy/SchedulerStrategyProviderTest.java
@@ -7,13 +7,13 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-class SchedulerStrategyFactoryTest {
+class SchedulerStrategyProviderTest {
private static final String FIRST_COME_FIRST_SERVE = "first-come-first-serve";
private static final String ONLY_ONE_SCAN_PER_PROJECT = "only-one-scan-per-project-at-a-time";
private static final String ONLY_ONE_SCAN_PER_PROJECT_AND_MODULE_GROUP = "only-one-scan-per-project-and-module-group";
- private SchedulerStrategyFactory factoryToTest;
+ private SchedulerStrategyProvider factoryToTest;
private FirstComeFirstServeSchedulerStrategy firstComeFirstServeStrategy;
private OnlyOneScanPerProjectAtSameTimeStrategy onlyOneScanPerProjectStrategy;
private OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy onlyOneScanPerProjectAndModuleGroupStrategy;
@@ -25,7 +25,7 @@ void beforeEach() {
onlyOneScanPerProjectStrategy = mock(OnlyOneScanPerProjectAtSameTimeStrategy.class);
onlyOneScanPerProjectAndModuleGroupStrategy = mock(OnlyOneScanPerProjectAndModuleGroupAtSameTimeStrategy.class);
- factoryToTest = new SchedulerStrategyFactory();
+ factoryToTest = new SchedulerStrategyProvider();
factoryToTest.firstComeFirstServeStrategy = firstComeFirstServeStrategy;
factoryToTest.onlyOneScanPerProjectStrategy = onlyOneScanPerProjectStrategy;
diff --git a/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubServerFlywayFactory.java b/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubServerFlywayFactory.java
index b3f1ba2be8..669dd29279 100644
--- a/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubServerFlywayFactory.java
+++ b/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubServerFlywayFactory.java
@@ -14,6 +14,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
@@ -24,7 +25,7 @@ public class SecHubServerFlywayFactory {
private static final Logger LOG = LoggerFactory.getLogger(SecHubServerFlywayFactory.class);
@Value("${sechub.migration.flyway.autorepair:true}")
- @MustBeDocumented(value = "When enabled, flyway migration problems will be automatically repaired", scope = "migration")
+ @MustBeDocumented(value = "When enabled, flyway migration problems will be automatically repaired", scope = DocumentationScopeConstants.SCOPE_MIGRATION)
boolean repairAutomatically;
@Bean
diff --git a/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubSystemPropertyInjector.java b/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubSystemPropertyInjector.java
index 9bf1c8ffa1..67ecf11e62 100644
--- a/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubSystemPropertyInjector.java
+++ b/sechub-server/src/main/java/com/mercedesbenz/sechub/server/SecHubSystemPropertyInjector.java
@@ -4,6 +4,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import jakarta.annotation.PostConstruct;
@@ -19,7 +20,7 @@
@Component
public class SecHubSystemPropertyInjector {
- @MustBeDocumented(value = "Define diffie hellman key length, see https://github.com/mercedes-benz/sechub/issues/689 for details", scope = "security")
+ @MustBeDocumented(value = "Define diffie hellman key length, see https://github.com/mercedes-benz/sechub/issues/689 for details", scope = DocumentationScopeConstants.SCOPE_SECURITY)
@Value("${sechub.security.diffiehellman.length}")
private String diffieHellmanLength;
diff --git a/sechub-server/src/test/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerServiceTest.java b/sechub-server/src/test/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerServiceTest.java
index f43c0c9165..153ff04608 100644
--- a/sechub-server/src/test/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerServiceTest.java
+++ b/sechub-server/src/test/java/com/mercedesbenz/sechub/domain/schedule/ScheduleJobMarkerServiceTest.java
@@ -14,14 +14,11 @@
import com.mercedesbenz.sechub.commons.model.job.ExecutionState;
import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
-import com.mercedesbenz.sechub.domain.schedule.strategy.FirstComeFirstServeSchedulerStrategy;
-import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerStrategyFactory;
+import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerNextJobResolver;
public class ScheduleJobMarkerServiceTest {
private SecHubJobRepository jobRepository;
- private SchedulerStrategyFactory factory;
- private FirstComeFirstServeSchedulerStrategy strategy;
private ScheduleSecHubJob secHubJob;
@@ -29,44 +26,44 @@ public class ScheduleJobMarkerServiceTest {
private ScheduleJobMarkerService serviceToTest;
+ private SchedulerNextJobResolver nextJobResolver;
+
@Before
public void before() throws Exception {
serviceToTest = new ScheduleJobMarkerService();
- factory = mock(SchedulerStrategyFactory.class);
- strategy = mock(FirstComeFirstServeSchedulerStrategy.class);
jobRepository = mock(SecHubJobRepository.class);
-
+ nextJobResolver = mock(SchedulerNextJobResolver.class);
uuid = UUID.randomUUID();
serviceToTest.jobRepository = jobRepository;
- serviceToTest.schedulerStrategyFactory = factory;
- strategy.jobRepository = jobRepository;
+ serviceToTest.nextJobResolver = nextJobResolver;
secHubJob = mock(ScheduleSecHubJob.class);
- when(factory.build()).thenReturn(strategy);
- when(strategy.nextJobId()).thenReturn(uuid);
- when(jobRepository.getJob(uuid)).thenReturn(Optional.of(secHubJob));
+ when(jobRepository.getJobWhenExecutable(uuid)).thenReturn(Optional.of(secHubJob));
}
@Test
- public void markNextJobExecutedByThisPOD__calls_jobrepository_getjob_executed() throws Exception {
+ public void markNextJobExecutedByThisPOD__calls_nextJobResolver() throws Exception {
/* execute */
serviceToTest.markNextJobToExecuteByThisInstance();
/* test */
- verify(jobRepository).getJob(uuid);
+ verify(nextJobResolver).resolveNextJob();
}
@Test
- public void markNextJobExecutedByThisPOD__updates_execution_state_to_started() throws Exception {
+ public void markNextJobExecutedByThisPOD__next_job_found_updates_execution_state_to_started() throws Exception {
/* prepare */
+ when(nextJobResolver.resolveNextJob()).thenReturn(uuid);
when(jobRepository.save(secHubJob)).thenReturn(secHubJob);
/* execute */
ScheduleSecHubJob result = serviceToTest.markNextJobToExecuteByThisInstance();
/* test */
+ verify(nextJobResolver).resolveNextJob();
+ verify(jobRepository).save(secHubJob);
verify(secHubJob).setStarted(any());
verify(secHubJob).setExecutionState(eq(ExecutionState.STARTED));
@@ -74,4 +71,18 @@ public void markNextJobExecutedByThisPOD__updates_execution_state_to_started() t
assertEquals(secHubJob, result);
}
+ @Test
+ public void markNextJobExecutedByThisPOD__next_job_not_found() throws Exception {
+ /* prepare */
+ when(nextJobResolver.resolveNextJob()).thenReturn(null);
+
+ /* execute */
+ ScheduleSecHubJob result = serviceToTest.markNextJobToExecuteByThisInstance();
+
+ /* test */
+ verify(nextJobResolver).resolveNextJob();
+ verifyNoInteractions(jobRepository);
+ assertEquals(null,result);
+ }
+
}
diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/DocumentationScopeConstants.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/DocumentationScopeConstants.java
new file mode 100644
index 0000000000..20c0965c07
--- /dev/null
+++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/DocumentationScopeConstants.java
@@ -0,0 +1,21 @@
+package com.mercedesbenz.sechub.sharedkernel;
+
+public class DocumentationScopeConstants {
+
+ /**
+ * If this scope is used, it shall be replaced in generated output by lower
+ * cased class name of class where annotation is used
+ */
+ public static final String SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED = "definingClassNameToLowercase";
+
+ public static final String SCOPE_SCHEDULE = "schedule";
+
+ public static final String SCOPE_ADMINISTRATION = "administration";
+
+ public static final String SCOPE_MIGRATION = "migration";
+
+ public static final String SCOPE_SECURITY = "security";
+
+ public static final String SCOPE_STORAGE = "storage";
+
+}
diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/MustBeDocumented.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/MustBeDocumented.java
index f8caaedf92..ec3905ee91 100644
--- a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/MustBeDocumented.java
+++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/MustBeDocumented.java
@@ -27,12 +27,6 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface MustBeDocumented {
- /**
- * If this scope is used, it shall be replaced in generated output by lower
- * cased class name of class where annotation is used
- */
- String SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED = "definingClassNameToLowercase";
-
/**
* A description what the documented part is used for
*
@@ -41,13 +35,15 @@
String value() default "";
/**
- * The scope name for the documentation - when not set
- * {@link #SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED} is used as scope name. Thus
- * information can be used for generating documentation and separate groups etc.
+ * The scope name for the documentation - default fallback value is
+ * {@link DocumentationScopeConstants#SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED}
+ * (will result in a generated scope name depending on annotated class package).
+ * The scope information is used for generating documentation and separate
+ * groups etc.
*
* @return scope
*/
- String scope() default SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED;
+ String scope() default DocumentationScopeConstants.SCOPE_USE_DEFINED_CLASSNAME_LOWERCASED;
/**
* When true
the information of this annotation must be handled
diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/S3PropertiesSetup.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/S3PropertiesSetup.java
index 123fcb6fcd..612b543f8d 100644
--- a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/S3PropertiesSetup.java
+++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/S3PropertiesSetup.java
@@ -4,6 +4,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.storage.core.S3Setup;
@@ -11,61 +12,61 @@
public class S3PropertiesSetup implements S3Setup {
private static final String UNDEFINED = "undefined";
- @MustBeDocumented(value = "Defines the access key for used S3 bucket", scope = "storage", secret = true)
+ @MustBeDocumented(value = "Defines the access key for used S3 bucket", scope = DocumentationScopeConstants.SCOPE_STORAGE, secret = true)
@Value("${sechub.storage.s3.accesskey:" + UNDEFINED + "}") // we use undefined here. Will be used in isValid
private String accessKey;
- @MustBeDocumented(value = "Defines the secret key for used S3 bucket", scope = "storage", secret = true)
+ @MustBeDocumented(value = "Defines the secret key for used S3 bucket", scope = DocumentationScopeConstants.SCOPE_STORAGE, secret = true)
@Value("${sechub.storage.s3.secretkey:" + UNDEFINED + "}") // we use undefined here. Will be used in isValid
private String secretKey;
- @MustBeDocumented(value = "Defines the S3 bucket name", scope = "storage")
+ @MustBeDocumented(value = "Defines the S3 bucket name", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.bucketname:" + UNDEFINED + "}") // we use undefined here. Will be used in isValid
private String bucketName;
- @MustBeDocumented(value = "Defines the S3 endpoint - e.g. https://play.min.io", scope = "storage")
+ @MustBeDocumented(value = "Defines the S3 endpoint - e.g. https://play.min.io", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.endpoint:" + UNDEFINED + "}") // we use undefined here. Will be used in isValid
private String endpoint;
/* timeout */
- @MustBeDocumented(value = "S3 client timeout (in milliseconds) for creating new connections.", scope = "storage")
+ @MustBeDocumented(value = "S3 client timeout (in milliseconds) for creating new connections.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.timeout.connection.milliseconds:" + S3Setup.DEFAULT_CONNECTION_TIMEOUT + "}")
private int connectionTimeoutInMilliseconds;
- @MustBeDocumented(value = "S3 client timeout (in milliseconds) for reading from a connected socket.", scope = "storage")
+ @MustBeDocumented(value = "S3 client timeout (in milliseconds) for reading from a connected socket.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.timeout.socket.milliseconds:" + S3Setup.DEFAULT_SOCKET_TIMEOUT + "}")
private int socketTimeoutInMilliseconds;
- @MustBeDocumented(value = "S3 client timeout (in milliseconds) for a request. 0 means it is disabled.", scope = "storage")
+ @MustBeDocumented(value = "S3 client timeout (in milliseconds) for a request. 0 means it is disabled.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.timeout.request.milliseconds:" + S3Setup.DEFAULT_REQUEST_TIMEOUT + "}")
private int requestTimeoutInMilliseconds;
- @MustBeDocumented(value = "S3 client timeout (in milliseconds) for execution. 0 means it is disabled.", scope = "storage")
+ @MustBeDocumented(value = "S3 client timeout (in milliseconds) for execution. 0 means it is disabled.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.timeout.execution.milliseconds:" + S3Setup.DEFAULT_CLIENT_EXECUTION_TIMEOUT + "}")
private int clientExecutionTimeoutInMilliseconds;
/* connections */
- @MustBeDocumented(value = "S3 client max connection pool size.", scope = "storage")
+ @MustBeDocumented(value = "S3 client max connection pool size.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.connection.max.poolsize:" + S3Setup.DEFAULT_MAX_CONNECTIONS + "}")
private int maximumAllowedConnections;
- @MustBeDocumented(value = "S3 client expiration time (in milliseconds) for a connection in the connection pool. -1 means deactivated", scope = "storage")
+ @MustBeDocumented(value = "S3 client expiration time (in milliseconds) for a connection in the connection pool. -1 means deactivated", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.connection.ttl.milliseconds:" + S3Setup.DEFAULT_CONNECTION_TTL + "}")
private long connectionTTLInMilliseconds;
- @MustBeDocumented(value = "S3 client maximum idle time (in milliseconds) for a connection in the connection pool.", scope = "storage")
+ @MustBeDocumented(value = "S3 client maximum idle time (in milliseconds) for a connection in the connection pool.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.connection.idle.max.milliseconds:" + S3Setup.DEFAULT_CONNECTION_MAX_IDLE_MILLIS + "}")
private long connectionMaxIdleInMilliseconds;
- @MustBeDocumented(value = "S3 client time (in milliseconds) a connection can be idle in the connection pool before it must be validated that it's still open.", scope = "storage")
+ @MustBeDocumented(value = "S3 client time (in milliseconds) a connection can be idle in the connection pool before it must be validated that it's still open.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.connection.idle.validate.milliseconds:" + S3Setup.DEFAULT_VALIDATE_AFTER_INACTIVITY_MILLIS + "}")
private int validateAfterInactivityInMilliseconds;
/* signer */
- @MustBeDocumented(value = "Can be used to override the default name of the signature algorithm used to sign requests.", scope = "storage")
+ @MustBeDocumented(value = "Can be used to override the default name of the signature algorithm used to sign requests.", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.s3.signer.override:" + S3Setup.DEFAULT_SIGNER_OVERRIDE + "}")
private String signerOverride;
diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/SharedVolumePropertiesSetup.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/SharedVolumePropertiesSetup.java
index 7a6ec702fc..f84d250f03 100644
--- a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/SharedVolumePropertiesSetup.java
+++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/storage/SharedVolumePropertiesSetup.java
@@ -4,6 +4,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
+import com.mercedesbenz.sechub.sharedkernel.DocumentationScopeConstants;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.storage.sharevolume.spring.AbstractSharedVolumePropertiesSetup;
@@ -14,7 +15,7 @@ public class SharedVolumePropertiesSetup extends AbstractSharedVolumePropertiesS
* Folder location for storing files. When using "temp" a temporary folder on
* server side will be used
*/
- @MustBeDocumented(value = "Defines the root path for shared volume uploads - e.g. for sourcecode.zip etc. When using keyword *temp* as path, this will create a temporary directory (for testing).", scope = "storage")
+ @MustBeDocumented(value = "Defines the root path for shared volume uploads - e.g. for sourcecode.zip etc. When using keyword *temp* as path, this will create a temporary directory (for testing).", scope = DocumentationScopeConstants.SCOPE_STORAGE)
@Value("${sechub.storage.sharedvolume.upload.dir:" + UNDEFINED + "}") // we use undefined here. Will be used in #isValid()
private String configuredUploadDir = UNDEFINED;;