From 39cf8d9cfb64ed45ff325eeb3218ffe191e9c7f8 Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Wed, 1 Jul 2026 09:39:14 +0300 Subject: [PATCH 1/3] Adding user info related information regarding deployment process actions LMCROSSITXSADEPLOY-3329 --- .../controller/process/Constants.java | 1 + .../controller/process/Messages.java | 3 +- .../process/flowable/AbortProcessAction.java | 3 +- .../process/flowable/ProcessAction.java | 19 +++++++++ .../process/flowable/ResumeProcessAction.java | 5 +++ .../process/flowable/RetryProcessAction.java | 5 +++ .../AbortProcessFlowableCommandExecutor.java | 2 +- .../process/jobs/OperationsCleaner.java | 10 ++--- .../flowable/AbortProcessActionTest.java | 19 +++++++++ .../process/flowable/ProcessActionTest.java | 40 ++++++++++++++++++- .../flowable/ResumeProcessActionTest.java | 15 +++++++ .../flowable/RetryProcessActionTest.java | 16 ++++++++ .../flowable/StartProcessActionTest.java | 16 ++++++++ .../multiapps/controller/web/Constants.java | 1 + .../multiapps/controller/web/Messages.java | 1 + .../api/impl/OperationsApiServiceImpl.java | 35 +++++++++------- .../impl/OperationsApiServiceImplTest.java | 8 +++- 17 files changed, 174 insertions(+), 25 deletions(-) mode change 100644 => 100755 multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Constants.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Constants.java index 2595cdd1c5..7ff3d2c76a 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Constants.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Constants.java @@ -36,6 +36,7 @@ public class Constants { public static final String TRIPLE_APPENDED_STRING = "%s%s%s"; public static final String SECURE_EXTENSION_DESCRIPTOR_ID = "__mta.secure"; public static final String STRING_SEPARATOR = "-"; + public static final String AUTH_TOKEN_ORIGIN_FIELD = "origin"; public static final Long UNSET_LAST_LOG_TIMESTAMP_MS = 0L; public static final int LOG_STALLED_TASK_MINUTE_INTERVAL = 5; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java old mode 100644 new mode 100755 index 1a3fb90642..40b0cca338 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java @@ -295,7 +295,8 @@ public class Messages { public static final String UNBINDING_SERVICE_INSTANCE_FROM_APP = "Unbinding service instance \"{0}\" from application \"{1}\"..."; public static final String UNBINDING_SERVICE_INSTANCE_FROM_APP_FINISHED = "Unbinding service instance \"{0}\" from application \"{1}\" finished"; public static final String POLLING_SERVICE_OPERATIONS = "Polling service operations..."; - public static final String AUTO_ABORTING_PROCESS_0 = "Auto-aborting process \"{0}\"..."; + public static final String AUTO_ABORTING_PROCESS_0 = "Auto-aborting process with processId: \"{0}\", action: \"{1}\", actor: system ..."; + public static final String DEPLOYMENT_PROCESS_ACTION_EXECUTED_FOR_PROCESS_0_ACTION_1_USER_2_ORIGIN_3 = "MTA deployment process action executed - processId: \"{0}\", action: \"{1}\", userGUID: \"{2}\", origin: \"{3}\""; public static final String SOME_INSTANCES_ARE_DOWN = "Some instances are down. Check the application logs of your application for details."; public static final String SOME_INSTANCES_HAVE_CRASHED = "Some instances have crashed. Check the application logs of your application for details."; public static final String CLEAN_UP_JOB_STARTED_BY_APPLICATION_INSTANCE_0_AT_1 = "Clean-up job started by application instance {0} at: {1}"; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessAction.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessAction.java index 39d7f3479a..ee52ae4a0e 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessAction.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessAction.java @@ -90,7 +90,8 @@ public Action getAction() { return Action.ABORT; } - private Logger getLogger() { + @Override + protected Logger getLogger() { return LOGGER; } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessAction.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessAction.java index 79c6b5fa62..9327e4b849 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessAction.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessAction.java @@ -1,5 +1,6 @@ package org.cloudfoundry.multiapps.controller.process.flowable; +import java.text.MessageFormat; import java.util.List; import java.util.stream.Collectors; @@ -9,11 +10,14 @@ import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientProvider; import org.cloudfoundry.multiapps.controller.core.util.UserInfo; import org.cloudfoundry.multiapps.controller.persistence.services.OperationService; +import org.cloudfoundry.multiapps.controller.process.Constants; +import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.util.ClientReleaser; import org.cloudfoundry.multiapps.controller.process.util.HistoryUtil; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; +import org.slf4j.Logger; public abstract class ProcessAction { @@ -38,6 +42,10 @@ protected List getActiveExecutionIds(String superProcessInstanceId) { } public void execute(UserInfo userInfo, String superProcessInstanceId) { + if (userInfo != null) { + logLifecycle(userInfo, superProcessInstanceId); + } + for (AdditionalProcessAction additionalProcessAction : filterAdditionalActionsForThisAction()) { additionalProcessAction.executeAdditionalProcessAction(superProcessInstanceId); } @@ -86,4 +94,15 @@ protected void updateOperationState(String processId, Operation.State newState) .build(); operationService.update(operation, operation); } + + protected abstract Logger getLogger(); + + protected void logLifecycle(UserInfo userInfo, String processId) { + getLogger().info(MessageFormat.format( + Messages.DEPLOYMENT_PROCESS_ACTION_EXECUTED_FOR_PROCESS_0_ACTION_1_USER_2_ORIGIN_3, + processId, getAction().toString(), userInfo.getId(), userInfo.getToken() + .getAdditionalInfo() + .get(Constants.AUTH_TOKEN_ORIGIN_FIELD))); + } + } \ No newline at end of file diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessAction.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessAction.java index abcbaff3ea..05755207b4 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessAction.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessAction.java @@ -53,6 +53,11 @@ private void triggerProcessInstance(UserInfo userInfo, String processId) { } } + @Override + protected Logger getLogger() { + return LOGGER; + } + @Override public Action getAction() { return Action.RESUME; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessAction.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessAction.java index 41d7923499..1b8802b3d1 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessAction.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessAction.java @@ -60,4 +60,9 @@ public Action getAction() { return Action.RETRY; } + @Override + protected Logger getLogger() { + return LOGGER; + } + } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java index b5bd04af27..b350304408 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java @@ -23,7 +23,7 @@ public AbortProcessFlowableCommandExecutor(ProcessActionRegistry processActionRe @Override public void executeCommand() { - LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processInstanceId)); + LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processInstanceId, Action.ABORT.toString())); ProcessAction abortProcessAction = processActionRegistry.getAction(Action.ABORT); abortProcessAction.execute(null, processInstanceId); } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java index 588d77b5c7..89a60e1abb 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java @@ -1,14 +1,12 @@ package org.cloudfoundry.multiapps.controller.process.jobs; -import static java.text.MessageFormat.format; - +import java.text.MessageFormat; import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.util.List; import jakarta.inject.Inject; import jakarta.inject.Named; - import org.cloudfoundry.multiapps.controller.api.model.ImmutableOperation; import org.cloudfoundry.multiapps.controller.api.model.Operation; import org.cloudfoundry.multiapps.controller.persistence.OrderDirection; @@ -18,11 +16,12 @@ import org.cloudfoundry.multiapps.controller.process.flowable.ProcessAction; import org.cloudfoundry.multiapps.controller.process.flowable.ProcessActionRegistry; import org.flowable.common.engine.api.FlowableObjectNotFoundException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; +import static java.text.MessageFormat.format; + @Named @Order(10) public class OperationsCleaner implements Cleaner { @@ -59,7 +58,7 @@ public void execute(LocalDateTime expirationTime) { private int abortActiveOperations(LocalDateTime expirationTime) { int abortedOperations = 0; - for (int pageIndex = 0;; pageIndex++) { + for (int pageIndex = 0; ; pageIndex++) { List operationsPage = getOperationsPage(expirationTime, pageIndex); for (Operation operation : operationsPage) { if (inFinalState(operation)) { @@ -107,6 +106,7 @@ private void abort(Operation operation) { ProcessAction abortAction = processActionRegistry.getAction(Action.ABORT); String processId = operation.getProcessId(); LOGGER.debug(CleanUpJob.LOG_MARKER, format(Messages.ABORTING_OPERATION_0, processId)); + LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processId, Action.ABORT.toString())); abortAction.execute(null, processId); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessActionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessActionTest.java index 6e958082a5..bb79f55fd0 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessActionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/AbortProcessActionTest.java @@ -23,6 +23,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; +import org.slf4j.Logger; class AbortProcessActionTest extends ProcessActionTest { @@ -46,6 +47,7 @@ class AbortProcessActionTest extends ProcessActionTest { .processType(ProcessType.DEPLOY) .state(Operation.State.RUNNING) .build(); + @BeforeEach void setUp() { prepareOperationService(); @@ -86,6 +88,23 @@ void testAbortActionNoPublishingDynatraceEvent() { .publishProcessEvent(Mockito.any(), Mockito.any()); } + @Test + void testAbortActionWithUserGuidAndOriginButNotUsername() { + Logger logger = Mockito.mock(Logger.class); + ProcessAction abortAction = new AbortProcessAction(flowableFacade, Collections.emptyList(), + historicOperationEventService, operationService, + cloudControllerClientProvider, progressMessageService, + dynatracePublisher) { + @Override + protected Logger getLogger() { + return logger; + } + }; + + abortAction.execute(USER_INFO, PROCESS_GUID); + assertUserInfoLogContent(logger, Action.ABORT); + } + private void prepareOperationService() { OperationQuery mockedOperationQuery = Mockito.mock(OperationQuery.class); Mockito.when(mockedOperationQuery.processId(PROCESS_GUID)) diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessActionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessActionTest.java index 5a2094081c..5f4144315c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessActionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ProcessActionTest.java @@ -2,11 +2,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.function.Supplier; import org.cloudfoundry.multiapps.controller.api.model.ImmutableOperation; import org.cloudfoundry.multiapps.controller.api.model.Operation; +import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo; import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientProvider; import org.cloudfoundry.multiapps.controller.core.util.UserInfo; import org.cloudfoundry.multiapps.controller.persistence.query.impl.OperationQueryImpl; @@ -17,10 +19,14 @@ import org.flowable.engine.runtime.Execution; import org.flowable.variable.api.history.HistoricVariableInstanceQuery; import org.junit.jupiter.api.BeforeEach; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; abstract class ProcessActionTest { @@ -31,7 +37,8 @@ abstract class ProcessActionTest { static final String PROCESS_GUID = RANDOM_UUID_SUPPLIER.get(); static final String SUBPROCESS_1_ID = RANDOM_UUID_SUPPLIER.get(); static final String SUBPROCESS_2_ID = RANDOM_UUID_SUPPLIER.get(); - static final UserInfo USER_INFO = new UserInfo("fake-user-guid", "fake-user", null); + static final OAuth2AccessTokenWithAdditionalInfo token = mockToken(); + static final UserInfo USER_INFO = new UserInfo("fake-user-guid", "fake-user", token); protected ProcessAction processAction; @Mock @@ -124,5 +131,36 @@ protected void assertStateUpdated(Operation.State state) { .update(operation, operation); } + protected void assertUserInfoLogContent(Logger logger, Action expectedAction) { + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + Mockito.verify(logger, Mockito.atLeastOnce()) + .info(argumentCaptor.capture()); + String logMessageToVerify = argumentCaptor.getAllValues() + .stream() + .filter(message -> message != null && message.contains( + "MTA deployment process action executed")) + .findFirst() + .orElseThrow( + () -> new AssertionError("No log line was printed. Captured logs: " + + argumentCaptor.getAllValues())); + assertTrue(logMessageToVerify.contains("processId: \"" + PROCESS_GUID + "\""), + "Must contain the processId. Actual: " + logMessageToVerify); + assertTrue(logMessageToVerify.contains("action: \"" + expectedAction + "\""), + "Must contain the action. Actual: " + logMessageToVerify); + assertTrue(logMessageToVerify.contains("userGUID: \"fake-user-guid\""), + "Must contain the userGUID. Actual: " + logMessageToVerify); + assertTrue(logMessageToVerify.contains("origin: \"test-origin\""), + "Must contain the origin. Actual: " + logMessageToVerify); + assertFalse(logMessageToVerify.contains("fake-user\""), + "Must NOT contain the username. Actual: " + logMessageToVerify); + } + protected abstract ProcessAction createProcessAction(); + + private static OAuth2AccessTokenWithAdditionalInfo mockToken() { + OAuth2AccessTokenWithAdditionalInfo token = Mockito.mock(OAuth2AccessTokenWithAdditionalInfo.class); + Mockito.when(token.getAdditionalInfo()) + .thenReturn(Map.of("origin", "test-origin")); + return token; + } } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessActionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessActionTest.java index 1454e50915..a6306dad86 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessActionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/ResumeProcessActionTest.java @@ -9,6 +9,7 @@ import org.flowable.engine.runtime.Execution; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.slf4j.Logger; import static org.mockito.Mockito.times; @@ -38,4 +39,18 @@ void testResumeExecutionWithoutExecutionsAtReceiveTask() { protected ProcessAction createProcessAction() { return new ResumeProcessAction(flowableFacade, Collections.emptyList(), operationService, cloudControllerClientProvider); } + + @Test + void testResumeActionWithUserGuidAndOriginButNotUsername() { + Logger logger = Mockito.mock(Logger.class); + ProcessAction resumeAction = new ResumeProcessAction(flowableFacade, Collections.emptyList(), operationService, + cloudControllerClientProvider) { + @Override + protected Logger getLogger() { + return logger; + } + }; + resumeAction.execute(USER_INFO, PROCESS_GUID); + assertUserInfoLogContent(logger, Action.RESUME); + } } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessActionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessActionTest.java index 5d2a85a1c7..7f863d9de7 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessActionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/RetryProcessActionTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; +import org.slf4j.Logger; class RetryProcessActionTest extends ProcessActionTest { @@ -48,6 +49,21 @@ private void verifySubprocessesAreExecuted() { .executeJob(SUBPROCESS_2_ID); } + @Test + void testRetryActionWithUserGuidAndOriginButNotUsername() { + Logger logger = Mockito.mock(Logger.class); + ProcessAction retryAction = new RetryProcessAction(flowableFacade, List.of(), historicOperationEventService, + operationService, cloudControllerClientProvider) { + @Override + protected Logger getLogger() { + return logger; + } + }; + + retryAction.execute(USER_INFO, PROCESS_GUID); + assertUserInfoLogContent(logger, Action.RETRY); + } + @Override protected ProcessAction createProcessAction() { return new RetryProcessAction(flowableFacade, diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/StartProcessActionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/StartProcessActionTest.java index ac33652655..db83f43868 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/StartProcessActionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/flowable/StartProcessActionTest.java @@ -7,6 +7,7 @@ import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.slf4j.Logger; import static org.mockito.Mockito.times; @@ -24,4 +25,19 @@ void testResumeExecution() { protected ProcessAction createProcessAction() { return new StartProcessAction(flowableFacade, Collections.emptyList(), operationService, cloudControllerClientProvider); } + + @Test + void testStartActionWithUserGuidAndOriginButNotUsername() { + Logger logger = Mockito.mock(Logger.class); + ProcessAction startAction = new StartProcessAction(flowableFacade, Collections.emptyList(), operationService, + cloudControllerClientProvider) { + @Override + protected Logger getLogger() { + return logger; + } + }; + + startAction.execute(USER_INFO, PROCESS_GUID); + assertUserInfoLogContent(logger, Action.START); + } } diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java index eea2d64634..fd46c65dd4 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java @@ -21,6 +21,7 @@ private Constants() { public static final String CSRF_PARAM_NAME = "X-CSRF-PARAM"; public static final String CSRF_HEADER_NAME = "X-CSRF-HEADER"; public static final String CONTENT_LENGTH = "Content-Length"; + public static final String AUTH_TOKEN_ORIGIN_FIELD = "origin"; public static final long OAUTH_TOKEN_RETENTION_TIME_IN_SECONDS = TimeUnit.MINUTES.toSeconds(2); public static final long BASIC_TOKEN_RETENTION_TIME_IN_SECONDS = TimeUnit.MINUTES.toSeconds(6); diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java index 2cce47657e..80987dd488 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java @@ -71,6 +71,7 @@ public final class Messages { public static final String OBJECT_STORE_WITH_PROVIDER_0_CREATED = "Object store with provider: {0} created"; public static final String JOB_WITH_ID_WAS_NOT_UPDATED_WITHIN_SECONDS = "Job with ID: {} was not updated within: {} seconds"; public static final String CLEARING_OLD_ENTRY = "Clearing old entry with id: {0}"; + public static final String STARTED_NEW_DEPLOYMENT_PROCESS = "Started new MTA deployment process with process instance id: \"{0}\" and origin of: \"{1}\", by user with user GUID: \"{2}\"."; // DEBUG log messages public static final String RECEIVED_UPLOAD_REQUEST = "Received upload request on URI: {}"; diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java index d25fdb32e2..08723f347f 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java @@ -1,5 +1,19 @@ package org.cloudfoundry.multiapps.controller.web.api.impl; +import java.text.MessageFormat; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.persistence.NoResultException; @@ -50,20 +64,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.server.ResponseStatusException; -import java.text.MessageFormat; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - import static org.cloudfoundry.multiapps.controller.persistence.Constants.VARIABLE_NAME_SERVICE_ID; import static org.cloudfoundry.multiapps.controller.web.Constants.NAMES_OF_SERVICE_PARAMETERS; @@ -181,6 +181,13 @@ public ResponseEntity startOperation(String spaceGuid, Operation oper operation = addParameterValues(operation, predefinedParameters); ensureRequiredParametersSet(operation, predefinedParameters); ProcessInstance processInstance = flowableFacade.startProcess(processDefinitionKey, operation.getParameters()); + LOGGER.info( + MessageFormat.format( + Messages.STARTED_NEW_DEPLOYMENT_PROCESS, processInstance.getProcessInstanceId(), authenticatedUser.getToken() + .getAdditionalInfo() + .get( + Constants.AUTH_TOKEN_ORIGIN_FIELD), + authenticatedUser.getId())); return ResponseEntity.accepted() .header("Location", getLocationHeader(processInstance.getProcessInstanceId(), spaceGuid)) diff --git a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java index 67b4ed7dc0..279256823e 100644 --- a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java +++ b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java @@ -25,6 +25,7 @@ import org.cloudfoundry.multiapps.controller.core.auditlogging.OperationsApiServiceAuditLog; import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientFactory; import org.cloudfoundry.multiapps.controller.core.security.token.TokenService; +import org.cloudfoundry.multiapps.controller.core.util.UserInfo; import org.cloudfoundry.multiapps.controller.persistence.query.OperationQuery; import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException; import org.cloudfoundry.multiapps.controller.persistence.services.OperationService; @@ -348,8 +349,11 @@ private void mockClientAuth(boolean shouldReturnAuthorizedClient) { org.springframework.security.core.context.SecurityContext.class); SecurityContextHolder.setContext(securityContextMock); if (shouldReturnAuthorizedClient) { - org.cloudfoundry.multiapps.controller.core.util.UserInfo userInfo = new org.cloudfoundry.multiapps.controller.core.util.UserInfo( - USER_GUID, EXAMPLE_USER, new OAuth2AccessTokenWithAdditionalInfo(null, Map.of(TokenProperties.USER_ID_KEY, USER_GUID))); + UserInfo userInfo = new UserInfo(USER_GUID, EXAMPLE_USER, new OAuth2AccessTokenWithAdditionalInfo(null, + Map.of( + TokenProperties.USER_ID_KEY, + USER_GUID, "origin", + "test-origin"))); OAuth2AuthenticationToken auth = Mockito.mock(OAuth2AuthenticationToken.class); Map attributes = Map.of(USER_INFO, userInfo); OAuth2User principal = Mockito.mock(OAuth2User.class); From f1ec58cee38dc21efbd7b890c3ed2136d05dc15c Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Thu, 2 Jul 2026 09:04:47 +0300 Subject: [PATCH 2/3] Fixing comments about passing action LMCROSSITXSADEPLOY-3329 --- .../org/cloudfoundry/multiapps/controller/process/Messages.java | 2 +- .../commands/abort/AbortProcessFlowableCommandExecutor.java | 2 +- .../multiapps/controller/process/jobs/OperationsCleaner.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java index 40b0cca338..6549708ff4 100755 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java @@ -295,7 +295,7 @@ public class Messages { public static final String UNBINDING_SERVICE_INSTANCE_FROM_APP = "Unbinding service instance \"{0}\" from application \"{1}\"..."; public static final String UNBINDING_SERVICE_INSTANCE_FROM_APP_FINISHED = "Unbinding service instance \"{0}\" from application \"{1}\" finished"; public static final String POLLING_SERVICE_OPERATIONS = "Polling service operations..."; - public static final String AUTO_ABORTING_PROCESS_0 = "Auto-aborting process with processId: \"{0}\", action: \"{1}\", actor: system ..."; + public static final String AUTO_ABORTING_PROCESS_0 = "Auto-aborting process with processId: \"{0}\", action: ABORT, actor: system ..."; public static final String DEPLOYMENT_PROCESS_ACTION_EXECUTED_FOR_PROCESS_0_ACTION_1_USER_2_ORIGIN_3 = "MTA deployment process action executed - processId: \"{0}\", action: \"{1}\", userGUID: \"{2}\", origin: \"{3}\""; public static final String SOME_INSTANCES_ARE_DOWN = "Some instances are down. Check the application logs of your application for details."; public static final String SOME_INSTANCES_HAVE_CRASHED = "Some instances have crashed. Check the application logs of your application for details."; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java index b350304408..b5bd04af27 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/flowable/commands/abort/AbortProcessFlowableCommandExecutor.java @@ -23,7 +23,7 @@ public AbortProcessFlowableCommandExecutor(ProcessActionRegistry processActionRe @Override public void executeCommand() { - LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processInstanceId, Action.ABORT.toString())); + LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processInstanceId)); ProcessAction abortProcessAction = processActionRegistry.getAction(Action.ABORT); abortProcessAction.execute(null, processInstanceId); } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java index 89a60e1abb..bb8d7615d4 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OperationsCleaner.java @@ -106,7 +106,7 @@ private void abort(Operation operation) { ProcessAction abortAction = processActionRegistry.getAction(Action.ABORT); String processId = operation.getProcessId(); LOGGER.debug(CleanUpJob.LOG_MARKER, format(Messages.ABORTING_OPERATION_0, processId)); - LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processId, Action.ABORT.toString())); + LOGGER.info(MessageFormat.format(Messages.AUTO_ABORTING_PROCESS_0, processId)); abortAction.execute(null, processId); } From 2de649f9caa7f8a9c8c36e8936932a2c1fd18687 Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Fri, 3 Jul 2026 13:06:42 +0300 Subject: [PATCH 3/3] Fixing comments and combining log calls --- .../multiapps/controller/web/Messages.java | 3 +- .../api/impl/OperationsApiServiceImpl.java | 21 +++++++----- .../impl/OperationsApiServiceImplTest.java | 34 +++++++++++++++++++ 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java index 80987dd488..71293fc740 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java @@ -71,7 +71,8 @@ public final class Messages { public static final String OBJECT_STORE_WITH_PROVIDER_0_CREATED = "Object store with provider: {0} created"; public static final String JOB_WITH_ID_WAS_NOT_UPDATED_WITHIN_SECONDS = "Job with ID: {} was not updated within: {} seconds"; public static final String CLEARING_OLD_ENTRY = "Clearing old entry with id: {0}"; - public static final String STARTED_NEW_DEPLOYMENT_PROCESS = "Started new MTA deployment process with process instance id: \"{0}\" and origin of: \"{1}\", by user with user GUID: \"{2}\"."; + public static final String STARTED_OPERATION_0_BY_USER_1_AND_ORIGIN_OF_2 = "Started operation \"{0}\" by user \"{1}\" and origin of \"{2}\"."; + ; // DEBUG log messages public static final String RECEIVED_UPLOAD_REQUEST = "Received upload request on URI: {}"; diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java index 08723f347f..0c88e07d57 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImpl.java @@ -172,7 +172,6 @@ public ResponseEntity getOperationLogContent(String spaceGuid, String op public ResponseEntity startOperation(String spaceGuid, Operation operation, HttpServletRequest httpServletRequest) { apiUsageLogger.logOperationsMutatingCall(spaceGuid, Constants.ApiEndpointsNames.START_OPERATION, null, operation.getNamespace(), httpServletRequest); - operationsApiServiceAuditLog.logStartOperation(SecurityContextUtil.getUsername(), spaceGuid, operation); UserInfo authenticatedUser = getAuthenticatedUser(); String processDefinitionKey = operationsHelper.getProcessDefinitionKey(operation); Set predefinedParameters = operationMetadataMapper.getOperationMetadata(operation.getProcessType()) @@ -181,19 +180,25 @@ public ResponseEntity startOperation(String spaceGuid, Operation oper operation = addParameterValues(operation, predefinedParameters); ensureRequiredParametersSet(operation, predefinedParameters); ProcessInstance processInstance = flowableFacade.startProcess(processDefinitionKey, operation.getParameters()); - LOGGER.info( - MessageFormat.format( - Messages.STARTED_NEW_DEPLOYMENT_PROCESS, processInstance.getProcessInstanceId(), authenticatedUser.getToken() - .getAdditionalInfo() - .get( - Constants.AUTH_TOKEN_ORIGIN_FIELD), - authenticatedUser.getId())); + logStartOperation(spaceGuid, operation, authenticatedUser, processInstance); return ResponseEntity.accepted() .header("Location", getLocationHeader(processInstance.getProcessInstanceId(), spaceGuid)) .build(); } + protected void logStartOperation(String processInstanceId, UserInfo authenticatedUser) { + LOGGER.info(MessageFormat.format(Messages.STARTED_OPERATION_0_BY_USER_1_AND_ORIGIN_OF_2, processInstanceId, + authenticatedUser.getId(), authenticatedUser.getToken() + .getAdditionalInfo() + .get(Constants.AUTH_TOKEN_ORIGIN_FIELD))); + } + + private void logStartOperation(String spaceGuid, Operation operation, UserInfo authenticatedUser, ProcessInstance processInstance) { + operationsApiServiceAuditLog.logStartOperation(SecurityContextUtil.getUsername(), spaceGuid, operation); + logStartOperation(processInstance.getProcessInstanceId(), authenticatedUser); + } + @Override public ResponseEntity getOperation(String spaceGuid, String operationId, String embed) { operationsApiServiceAuditLog.logGetOperation(SecurityContextUtil.getUsername(), spaceGuid, operationId, embed); diff --git a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java index 279256823e..ea44fb8882 100644 --- a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java +++ b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/OperationsApiServiceImplTest.java @@ -44,6 +44,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; @@ -212,6 +213,39 @@ void testStartOperation() { .startProcess(Mockito.any(), Mockito.anyMap()); } + @Test + void testStartOperationLogsUserGuidAndOriginButNotUsername() { + String processInstanceId = "process-instance-id-1"; + ProcessInstance processInstance = Mockito.mock(ProcessInstance.class); + Mockito.when(processInstance.getProcessInstanceId()) + .thenReturn(processInstanceId); + Mockito.when(flowableFacade.startProcess(Mockito.any(), Mockito.anyMap())) + .thenReturn(processInstance); + + Map parameters = Map.of(Variables.MTA_ID.getName(), "test"); + Operation operation = createOperation(null, null, parameters); + Mockito.when(operationsHelper.getProcessDefinitionKey(operation)) + .thenReturn("deploy"); + HttpServletRequest httpServletRequestMock = Mockito.mock(HttpServletRequest.class); + Mockito.when(httpServletRequestMock.getRequestURL()) + .thenReturn(new StringBuffer("test/api/path")); + + OperationsApiServiceImpl operationsApiServiceSpy = Mockito.spy(operationsApiService); + operationsApiServiceSpy.startOperation(SPACE_GUID, operation, httpServletRequestMock); + + ArgumentCaptor userInfoCaptor = ArgumentCaptor.forClass(UserInfo.class); + Mockito.verify(operationsApiServiceSpy) + .logStartOperation(Mockito.eq(processInstanceId), userInfoCaptor.capture()); + UserInfo authenticatedUser = userInfoCaptor.getValue(); + assertEquals(USER_GUID, authenticatedUser.getId(), "logStartOperation must receive the user GUID"); + assertEquals("test-origin", authenticatedUser.getToken() + .getAdditionalInfo() + .get("origin"), + "logStartOperation must receive a UserInfo whose token has the origin"); + Assertions.assertNotEquals(EXAMPLE_USER, authenticatedUser.getId(), + "logStartOperation must receive the user GUID and not the their username"); + } + @Test void testStartOperationWithInvalidParametersForTheProcess() { Map parameters = Map.of(Variables.MTA_ID.getName(), "test", Variables.EXT_DESCRIPTOR_FILE_ID.getName(), "ext_test",